Merge branch 'main' into reflect-auto-registration-staticlib

This commit is contained in:
eugineerd 2025-03-20 16:06:33 +00:00
commit eb66a7bcfc
377 changed files with 7537 additions and 3558 deletions

View File

@ -177,6 +177,31 @@ jobs:
- name: Check Compile
run: cargo check -p bevy --no-default-features --features default_no_std --target thumbv6m-none-eabi
check-compiles-no-std-examples:
runs-on: ubuntu-latest
timeout-minutes: 30
needs: ci
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
crates/bevy_ecs_compile_fail_tests/target/
crates/bevy_reflect_compile_fail_tests/target/
key: ${{ runner.os }}-cargo-check-compiles-no-std-examples-${{ hashFiles('**/Cargo.toml') }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-none
- name: Install Linux dependencies
uses: ./.github/actions/install-linux-deps
- name: Check Compile
run: cd examples/no_std/library && cargo check --no-default-features --features libm,critical-section --target x86_64-unknown-none
build-wasm:
runs-on: ubuntu-latest
timeout-minutes: 30
@ -234,7 +259,7 @@ jobs:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Run Markdown Lint
uses: docker://ghcr.io/github/super-linter:slim-v4
uses: super-linter/super-linter/slim@v7.3.0
env:
MULTI_STATUS: false
VALIDATE_ALL_CODEBASE: false
@ -267,7 +292,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Check for typos
uses: crate-ci/typos@v1.30.1
uses: crate-ci/typos@v1.30.2
- name: Typos info
if: failure()
run: |
@ -428,7 +453,7 @@ jobs:
shell: bash
run: |
errors=""
for file in $(find examples tests -name '*.rs'); do
for file in $(find examples tests -name '*.rs' -not -path 'examples/mobile/*'); do
if grep -q "use bevy_" "$file"; then
errors+="ERROR: Detected internal Bevy import in $file\n"
fi

View File

@ -26,6 +26,8 @@ members = [
"crates/bevy_reflect/compile_fail",
# Examples of compiling Bevy for mobile platforms.
"examples/mobile",
# Examples of using Bevy on no_std platforms.
"examples/no_std/*",
# Benchmarks
"benches",
# Internal tools that are not published.
@ -131,6 +133,7 @@ default = [
"bevy_audio",
"bevy_color",
"bevy_core_pipeline",
"bevy_anti_aliasing",
"bevy_gilrs",
"bevy_gizmos",
"bevy_gltf",
@ -211,6 +214,13 @@ bevy_core_pipeline = [
"bevy_render",
]
# Provides various anti aliasing solutions
bevy_anti_aliasing = [
"bevy_internal/bevy_anti_aliasing",
"bevy_asset",
"bevy_render",
]
# Adds gamepad support
bevy_gilrs = ["bevy_internal/bevy_gilrs"]
@ -223,6 +233,7 @@ bevy_pbr = [
"bevy_asset",
"bevy_render",
"bevy_core_pipeline",
"bevy_anti_aliasing",
]
# Provides picking functionality
@ -240,6 +251,7 @@ bevy_sprite = [
"bevy_render",
"bevy_core_pipeline",
"bevy_color",
"bevy_anti_aliasing",
]
# Provides text functionality
@ -252,6 +264,7 @@ bevy_ui = [
"bevy_text",
"bevy_sprite",
"bevy_color",
"bevy_anti_aliasing",
]
# Windowing layer
@ -278,6 +291,9 @@ bevy_log = ["bevy_internal/bevy_log"]
# Enable input focus subsystem
bevy_input_focus = ["bevy_internal/bevy_input_focus"]
# Use the configurable global error handler as the default error handler.
configurable_error_handler = ["bevy_internal/configurable_error_handler"]
# Enable passthrough loading for SPIR-V shaders (Only supported on Vulkan, shader capabilities and extensions must agree with the platform implementation)
spirv_shader_passthrough = ["bevy_internal/spirv_shader_passthrough"]
@ -2209,12 +2225,12 @@ category = "ECS (Entity Component System)"
wasm = false
[[example]]
name = "fallible_systems"
path = "examples/ecs/fallible_systems.rs"
name = "error_handling"
path = "examples/ecs/error_handling.rs"
doc-scrape-examples = true
required-features = ["bevy_mesh_picking_backend"]
required-features = ["bevy_mesh_picking_backend", "configurable_error_handler"]
[package.metadata.example.fallible_systems]
[package.metadata.example.error_handling]
name = "Fallible Systems"
description = "Systems that return results to handle errors"
category = "ECS (Entity Component System)"
@ -4266,3 +4282,39 @@ name = "Occlusion Culling"
description = "Demonstration of Occlusion Culling"
category = "3D Rendering"
wasm = false
[[example]]
name = "camera_controller"
path = "examples/helpers/camera_controller.rs"
doc-scrape-examples = true
crate-type = ["lib"]
[package.metadata.example.camera_controller]
name = "Camera Controller"
description = "Example Free-Cam Styled Camera Controller"
category = "Helpers"
wasm = true
[[example]]
name = "widgets"
path = "examples/helpers/widgets.rs"
doc-scrape-examples = true
crate-type = ["lib"]
[package.metadata.example.widgets]
name = "Widgets"
description = "Example UI Widgets"
category = "Helpers"
wasm = true
[[example]]
name = "no_std_library"
path = "examples/no_std/library/src/lib.rs"
doc-scrape-examples = true
crate-type = ["lib"]
[package.metadata.example.no_std_library]
name = "`no_std` Compatible Library"
description = "Example library compatible with `std` and `no_std` targets"
category = "Embedded"
wasm = true

View File

@ -1,4 +1,4 @@
import super::shaders::util::make_polka_dots;
import super::util::make_polka_dots;
struct VertexOutput {
@builtin(position) position: vec4<f32>,

View File

@ -1,29 +1,44 @@
fn make_polka_dots(pos: vec2<f32>, time: f32) -> vec4<f32> {
// Create repeating circles
let scaled_pos = pos * 6.0;
let cell = vec2<f32>(fract(scaled_pos.x), fract(scaled_pos.y));
let dist_from_center = distance(cell, vec2<f32>(0.5));
// Make dots alternate between pink and purple
var dist_from_center = distance(cell, vec2<f32>(0.5));
let is_even = (floor(scaled_pos.x) + floor(scaled_pos.y)) % 2.0;
var dot_color = vec3<f32>(0.0);
var is_dot = 0.0;
@if(!PARTY_MODE) {
let color1 = vec3<f32>(1.0, 0.4, 0.8); // pink
let color2 = vec3<f32>(0.6, 0.2, 1.0); // purple
dot_color = mix(color1, color2, is_even);
}
// Animate the colors in party mode
@if(PARTY_MODE) {
let color1 = vec3<f32>(1.0, 0.2, 0.2); // red
let color2 = vec3<f32>(0.2, 0.2, 1.0); // blue
let oscillation = (sin(time * 10.0) + 1.0) * 0.5;
let animated_color1 = mix(color1, color2, oscillation);
let animated_color2 = mix(color2, color1, oscillation);
is_dot = step(dist_from_center, 0.3);
} @else {
let grid_x = floor(scaled_pos.x);
let grid_y = floor(scaled_pos.y);
let wave_speed = 3.0;
let wave_phase = time * wave_speed;
let diagonal_pos = (grid_x + grid_y) * 0.5;
let wave_value = sin(diagonal_pos + wave_phase);
let wave_normalized = (wave_value + 1.0) * 0.5;
let color1 = vec3<f32>(1.0, 0.3, 0.7);
let color2 = vec3<f32>(0.5, 0.1, 1.0);
let intense_color1 = vec3<f32>(1.0, 0.1, 0.9);
let intense_color2 = vec3<f32>(0.8, 0.0, 1.0);
let animated_color1 = mix(color1, intense_color1, wave_normalized);
let animated_color2 = mix(color2, intense_color2, wave_normalized);
dot_color = mix(animated_color1, animated_color2, is_even);
let size_mod = 0.15 * wave_value;
dist_from_center = dist_from_center * (1.0 - size_mod);
// Animate whether something is a dot by position but also time
is_dot = step(dist_from_center, 0.3 + wave_normalized * 0.2);
}
// Draw the dot
let is_dot = step(dist_from_center, 0.3);
return vec4<f32>(dot_color * is_dot, is_dot);
}

View File

@ -10,7 +10,7 @@ use criterion::{
criterion_group!(
benches,
concrete_list_apply,
concrete_list_clone_dynamic,
concrete_list_to_dynamic_list,
dynamic_list_apply,
dynamic_list_push
);
@ -81,20 +81,20 @@ fn concrete_list_apply(criterion: &mut Criterion) {
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
patch(size).clone_dynamic()
patch(size).to_dynamic_list()
});
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
patch(size).clone_dynamic()
patch(size).to_dynamic_list()
});
group.finish();
}
fn concrete_list_clone_dynamic(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("concrete_list_clone_dynamic"));
fn concrete_list_to_dynamic_list(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("concrete_list_to_dynamic_list"));
for size in SIZES {
group.throughput(Throughput::Elements(size as u64));
@ -105,7 +105,7 @@ fn concrete_list_clone_dynamic(criterion: &mut Criterion) {
|bencher, &size| {
let v = iter::repeat_n(0, size).collect::<Vec<_>>();
bencher.iter(|| black_box(&v).clone_dynamic());
bencher.iter(|| black_box(&v).to_dynamic_list());
},
);
}
@ -127,7 +127,7 @@ fn dynamic_list_push(criterion: &mut Criterion) {
let dst = DynamicList::default();
bencher.iter_batched(
|| (src.clone(), dst.clone_dynamic()),
|| (src.clone(), dst.to_dynamic_list()),
|(src, mut dst)| {
for item in src {
dst.push(item);
@ -145,20 +145,20 @@ fn dynamic_list_push(criterion: &mut Criterion) {
fn dynamic_list_apply(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("dynamic_list_apply"));
let empty_base = |_: usize| || Vec::<u64>::new().clone_dynamic();
let empty_base = |_: usize| || Vec::<u64>::new().to_dynamic_list();
let full_base = |size: usize| move || iter::repeat_n(0, size).collect::<Vec<u64>>();
let patch = |size: usize| iter::repeat_n(1, size).collect::<Vec<u64>>();
list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch);
list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
patch(size).clone_dynamic()
patch(size).to_dynamic_list()
});
list_apply(&mut group, "same_len_concrete_patch", full_base, patch);
list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| {
patch(size).clone_dynamic()
patch(size).to_dynamic_list()
});
group.finish();

View File

@ -108,7 +108,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
);
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
key_range_patch(size).clone_dynamic()
key_range_patch(size).to_dynamic_map()
});
map_apply(
@ -122,7 +122,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
&mut group,
"same_keys_dynamic_patch",
key_range_base,
|size| key_range_patch(size).clone_dynamic(),
|size| key_range_patch(size).to_dynamic_map(),
);
map_apply(
@ -136,7 +136,7 @@ fn concrete_map_apply(criterion: &mut Criterion) {
&mut group,
"disjoint_keys_dynamic_patch",
key_range_base,
|size| disjoint_patch(size).clone_dynamic(),
|size| disjoint_patch(size).to_dynamic_map(),
);
}
@ -159,7 +159,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
(0..size as u64)
.zip(iter::repeat(0))
.collect::<HashMap<u64, u64>>()
.clone_dynamic()
.to_dynamic_map()
}
};
@ -183,7 +183,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
);
map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| {
key_range_patch(size).clone_dynamic()
key_range_patch(size).to_dynamic_map()
});
map_apply(
@ -197,7 +197,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
&mut group,
"same_keys_dynamic_patch",
key_range_base,
|size| key_range_patch(size).clone_dynamic(),
|size| key_range_patch(size).to_dynamic_map(),
);
map_apply(
@ -211,7 +211,7 @@ fn dynamic_map_apply(criterion: &mut Criterion) {
&mut group,
"disjoint_keys_dynamic_patch",
key_range_base,
|size| disjoint_patch(size).clone_dynamic(),
|size| disjoint_patch(size).to_dynamic_map(),
);
}

View File

@ -12,8 +12,8 @@ criterion_group!(
concrete_struct_apply,
concrete_struct_field,
concrete_struct_type_info,
concrete_struct_clone,
dynamic_struct_clone,
concrete_struct_to_dynamic_struct,
dynamic_struct_to_dynamic_struct,
dynamic_struct_apply,
dynamic_struct_get_field,
dynamic_struct_insert,
@ -113,7 +113,7 @@ fn concrete_struct_apply(criterion: &mut Criterion) {
bencher.iter_batched(
|| {
let (obj, _) = input();
let patch = obj.clone_dynamic();
let patch = obj.to_dynamic_struct();
(obj, patch)
},
|(mut obj, patch)| obj.apply(black_box(&patch)),
@ -170,8 +170,8 @@ fn concrete_struct_type_info(criterion: &mut Criterion) {
}
}
fn concrete_struct_clone(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("concrete_struct_clone"));
fn concrete_struct_to_dynamic_struct(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("concrete_struct_to_dynamic_struct"));
let structs: [(Box<dyn Struct>, Box<dyn Struct>); 5] = [
(
@ -203,28 +203,28 @@ fn concrete_struct_clone(criterion: &mut Criterion) {
BenchmarkId::new("NonGeneric", field_count),
&standard,
|bencher, s| {
bencher.iter(|| s.clone_dynamic());
bencher.iter(|| s.to_dynamic_struct());
},
);
group.bench_with_input(
BenchmarkId::new("Generic", field_count),
&generic,
|bencher, s| {
bencher.iter(|| s.clone_dynamic());
bencher.iter(|| s.to_dynamic_struct());
},
);
}
}
fn dynamic_struct_clone(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("dynamic_struct_clone"));
fn dynamic_struct_to_dynamic_struct(criterion: &mut Criterion) {
let mut group = create_group(criterion, bench!("dynamic_struct_to_dynamic_struct"));
let structs: [Box<dyn Struct>; 5] = [
Box::new(Struct1::default().clone_dynamic()),
Box::new(Struct16::default().clone_dynamic()),
Box::new(Struct32::default().clone_dynamic()),
Box::new(Struct64::default().clone_dynamic()),
Box::new(Struct128::default().clone_dynamic()),
Box::new(Struct1::default().to_dynamic_struct()),
Box::new(Struct16::default().to_dynamic_struct()),
Box::new(Struct32::default().to_dynamic_struct()),
Box::new(Struct64::default().to_dynamic_struct()),
Box::new(Struct128::default().to_dynamic_struct()),
];
for s in structs {
@ -234,7 +234,7 @@ fn dynamic_struct_clone(criterion: &mut Criterion) {
BenchmarkId::from_parameter(field_count),
&s,
|bencher, s| {
bencher.iter(|| s.clone_dynamic());
bencher.iter(|| s.to_dynamic_struct());
},
);
}
@ -265,7 +265,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) {
&patch,
|bencher, patch| {
bencher.iter_batched(
|| (base.clone_dynamic(), patch()),
|| (base.to_dynamic_struct(), patch()),
|(mut base, patch)| base.apply(black_box(&*patch)),
BatchSize::SmallInput,
);
@ -289,7 +289,7 @@ fn dynamic_struct_apply(criterion: &mut Criterion) {
}
bencher.iter_batched(
|| base.clone_dynamic(),
|| base.to_dynamic_struct(),
|mut base| base.apply(black_box(&patch)),
BatchSize::SmallInput,
);
@ -315,7 +315,7 @@ fn dynamic_struct_insert(criterion: &mut Criterion) {
let field = format!("field_{}", field_count);
bencher.iter_batched(
|| s.clone_dynamic(),
|| s.to_dynamic_struct(),
|mut s| {
s.insert(black_box(&field), ());
},

View File

@ -54,7 +54,11 @@ pub struct ActionRequest(pub accesskit::ActionRequest);
/// Useful if a third-party plugin needs to conditionally integrate with
/// `AccessKit`
#[derive(Resource, Default, Clone, Debug, Deref, DerefMut)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Resource))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, Resource)
)]
pub struct AccessibilityRequested(Arc<AtomicBool>);
impl AccessibilityRequested {
@ -78,7 +82,11 @@ impl AccessibilityRequested {
/// will generate conflicting updates.
#[derive(Resource, Clone, Debug, Deref, DerefMut)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Resource, Clone, Default)
)]
#[cfg_attr(
all(feature = "bevy_reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
@ -127,7 +135,7 @@ impl From<Node> for AccessibilityNode {
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(
all(feature = "bevy_reflect", feature = "serialize"),
reflect(Serialize, Deserialize)
reflect(Serialize, Deserialize, Clone)
)]
pub enum AccessibilitySystem {
/// Update the accessibility tree

View File

@ -111,6 +111,7 @@ impl<T> CubicKeyframeCurve<T> {
/// A keyframe-defined curve that uses cubic spline interpolation, special-cased for quaternions
/// since it uses `Vec4` internally.
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct CubicRotationCurve {
// Note: The sample width here should be 3.
core: ChunkedUnevenCore<Vec4>,
@ -374,6 +375,7 @@ impl<T> WideCubicKeyframeCurve<T> {
///
/// [`MorphWeights`]: bevy_render::prelude::MorphWeights
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum WeightsCurve {
/// A curve which takes a constant value over its domain. Notably, this is how animations with
/// only a single keyframe are interpreted.

View File

@ -108,7 +108,7 @@ use crate::{AnimationClip, AnimationTargetId};
///
/// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md
#[derive(Asset, Reflect, Clone, Debug, Serialize)]
#[reflect(Serialize, Debug)]
#[reflect(Serialize, Debug, Clone)]
#[serde(into = "SerializedAnimationGraph")]
pub struct AnimationGraph {
/// The `petgraph` data structure that defines the animation graph.
@ -131,7 +131,7 @@ pub struct AnimationGraph {
/// A [`Handle`] to the [`AnimationGraph`] to be used by the [`AnimationPlayer`](crate::AnimationPlayer) on the same entity.
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct AnimationGraphHandle(pub Handle<AnimationGraph>);
impl From<AnimationGraphHandle> for AssetId<AnimationGraph> {
@ -164,6 +164,7 @@ pub type AnimationNodeIndex = NodeIndex<u32>;
/// of the graph, contain animation clips to play. Blend and add nodes describe
/// how to combine their children to produce a final animation.
#[derive(Clone, Reflect, Debug)]
#[reflect(Clone)]
pub struct AnimationGraphNode {
/// Animation node data specific to the type of node (clip, blend, or add).
///
@ -205,6 +206,7 @@ pub struct AnimationGraphNode {
/// In the case of clip nodes, this contains the actual animation clip
/// associated with the node.
#[derive(Clone, Default, Reflect, Debug)]
#[reflect(Clone)]
pub enum AnimationNodeType {
/// A *clip node*, which plays an animation clip.
///

View File

@ -96,23 +96,26 @@ impl VariableCurve {
/// Because animation clips refer to targets by UUID, they can target any
/// [`AnimationTarget`] with that ID.
#[derive(Asset, Reflect, Clone, Debug, Default)]
#[reflect(Clone, Default)]
pub struct AnimationClip {
// This field is ignored by reflection because AnimationCurves can contain things that are not reflect-able
#[reflect(ignore)]
#[reflect(ignore, clone)]
curves: AnimationCurves,
events: AnimationEvents,
duration: f32,
}
#[derive(Reflect, Debug, Clone)]
#[reflect(Clone)]
struct TimedAnimationEvent {
time: f32,
event: AnimationEvent,
}
#[derive(Reflect, Debug, Clone)]
#[reflect(Clone)]
struct AnimationEvent {
#[reflect(ignore)]
#[reflect(ignore, clone)]
trigger: AnimationEventFn,
}
@ -124,6 +127,7 @@ impl AnimationEvent {
#[derive(Reflect, Clone)]
#[reflect(opaque)]
#[reflect(Clone, Default, Debug)]
struct AnimationEventFn(Arc<dyn Fn(&mut Commands, Entity, f32, f32) + Send + Sync>);
impl Default for AnimationEventFn {
@ -139,6 +143,7 @@ impl Debug for AnimationEventFn {
}
#[derive(Reflect, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
#[reflect(Clone)]
enum AnimationEventTarget {
Root,
Node(AnimationTargetId),
@ -172,6 +177,7 @@ pub type AnimationCurves = HashMap<AnimationTargetId, Vec<VariableCurve>, NoOpHa
///
/// [UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Reflect, Debug, Serialize, Deserialize)]
#[reflect(Clone)]
pub struct AnimationTargetId(pub Uuid);
impl Hash for AnimationTargetId {
@ -203,7 +209,7 @@ impl Hash for AnimationTargetId {
/// time. However, you can change [`AnimationTarget`]'s `player` property at
/// runtime to change which player is responsible for animating the entity.
#[derive(Clone, Copy, Component, Reflect)]
#[reflect(Component)]
#[reflect(Component, Clone)]
pub struct AnimationTarget {
/// The ID of this animation target.
///
@ -425,6 +431,7 @@ impl AnimationClip {
/// Repetition behavior of an animation.
#[derive(Reflect, Debug, PartialEq, Eq, Copy, Clone, Default)]
#[reflect(Clone, Default)]
pub enum RepeatAnimation {
/// The animation will finish after running once.
#[default]
@ -462,6 +469,7 @@ pub enum AnimationEvaluationError {
///
/// A stopped animation is considered no longer active.
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone, Default)]
pub struct ActiveAnimation {
/// The factor by which the weight from the [`AnimationGraph`] is multiplied.
weight: f32,
@ -674,7 +682,7 @@ impl ActiveAnimation {
/// Automatically added to any root animations of a scene when it is
/// spawned.
#[derive(Component, Default, Reflect)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct AnimationPlayer {
active_animations: HashMap<AnimationNodeIndex, ActiveAnimation>,
blend_weights: HashMap<AnimationNodeIndex, f32>,

View File

@ -29,7 +29,7 @@ use crate::{graph::AnimationNodeIndex, ActiveAnimation, AnimationPlayer};
/// component to get confused about which animation is the "main" animation, and
/// transitions will usually be incorrect as a result.
#[derive(Component, Default, Reflect)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct AnimationTransitions {
main_animation: Option<AnimationNodeIndex>,
transitions: Vec<AnimationTransition>,
@ -52,6 +52,7 @@ impl Clone for AnimationTransitions {
/// An animation that is being faded out as part of a transition
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone)]
pub struct AnimationTransition {
/// The current weight. Starts at 1.0 and goes to 0.0 during the fade-out.
current_weight: f32,

View File

@ -0,0 +1,40 @@
[package]
name = "bevy_anti_aliasing"
version = "0.16.0-dev"
edition = "2024"
description = "Provides various anti aliasing implementations for Bevy Engine"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
trace = []
webgl = []
webgpu = []
dds = ["bevy_render/dds", "bevy_image/dds"]
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" }
# other
tracing = { version = "0.1", default-features = false, features = ["std"] }
[lints]
workspace = true
[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true

View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,7 @@
# Bevy Anti Aliasing
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy_core_pipeline.svg)](https://crates.io/crates/bevy_core_pipeline)
[![Downloads](https://img.shields.io/crates/d/bevy_core_pipeline.svg)](https://crates.io/crates/bevy_core_pipeline)
[![Docs](https://docs.rs/bevy_core_pipeline/badge.svg)](https://docs.rs/bevy_core_pipeline/latest/bevy_core_pipeline/)
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)

View File

@ -1,10 +1,10 @@
use crate::{
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
};
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::BevyDefault as _;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
@ -36,7 +36,7 @@ pub use node::CasNode;
///
/// To use this, add the [`ContrastAdaptiveSharpening`] component to a 2D or 3D camera.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct ContrastAdaptiveSharpening {
/// Enable or disable sharpening.
pub enabled: bool,
@ -65,7 +65,7 @@ impl Default for ContrastAdaptiveSharpening {
}
#[derive(Component, Default, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct DenoiseCas(bool);
/// The uniform struct extracted from [`ContrastAdaptiveSharpening`] attached to a [`Camera`].

View File

@ -0,0 +1,9 @@
//! 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};
}

View File

@ -1,10 +1,10 @@
use crate::{
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
};
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_ecs::prelude::*;
use bevy_image::BevyDefault as _;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
@ -27,7 +27,7 @@ mod node;
pub use node::FxaaNode;
#[derive(Debug, Reflect, Eq, PartialEq, Hash, Clone, Copy)]
#[reflect(PartialEq, Hash)]
#[reflect(PartialEq, Hash, Clone)]
pub enum Sensitivity {
Low,
Medium,
@ -51,7 +51,7 @@ impl Sensitivity {
/// A component for enabling Fast Approximate Anti-Aliasing (FXAA)
/// for a [`bevy_render::camera::Camera`].
#[derive(Reflect, Component, Clone, ExtractComponent)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[extract_component_filter(With<Camera>)]
#[doc(alias = "FastApproximateAntiAliasing")]
pub struct Fxaa {

View File

@ -0,0 +1,27 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![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"
)]
use bevy_app::Plugin;
use contrast_adaptive_sharpening::CasPlugin;
use fxaa::FxaaPlugin;
use smaa::SmaaPlugin;
pub mod contrast_adaptive_sharpening;
pub mod experimental;
pub mod fxaa;
pub mod smaa;
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));
}
}

View File

@ -29,16 +29,16 @@
//! * Compatibility with SSAA and MSAA.
//!
//! [SMAA]: https://www.iryoku.com/smaa/
#[cfg(not(feature = "smaa_luts"))]
use crate::tonemapping::lut_placeholder;
use crate::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
};
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};
#[cfg(not(feature = "smaa_luts"))]
use bevy_core_pipeline::tonemapping::lut_placeholder;
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
@ -95,7 +95,7 @@ pub struct SmaaPlugin;
/// A component for enabling Subpixel Morphological Anti-Aliasing (SMAA)
/// for a [`bevy_render::camera::Camera`].
#[derive(Clone, Copy, Default, Component, Reflect, ExtractComponent)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[doc(alias = "SubpixelMorphologicalAntiAliasing")]
pub struct Smaa {
/// A predefined set of SMAA parameters: i.e. a quality level.
@ -110,7 +110,7 @@ pub struct Smaa {
///
/// The default value is *high*.
#[derive(Clone, Copy, Reflect, Default, PartialEq, Eq, Hash)]
#[reflect(Default)]
#[reflect(Default, Clone, PartialEq, Hash)]
pub enum SmaaPreset {
/// Four search steps; no diagonal or corner detection.
Low,

View File

@ -1,11 +1,11 @@
use crate::{
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
prelude::Camera3d,
prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_diagnostic::FrameCount;
use bevy_ecs::{
prelude::{Component, Entity, ReflectComponent},
@ -131,7 +131,7 @@ impl Plugin for TemporalAntiAliasPlugin {
///
/// If no [`MipBias`] component is attached to the camera, TAA will add a `MipBias(-1.0)` component.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[require(TemporalJitter, DepthPrepass, MotionVectorPrepass)]
#[doc(alias = "Taa")]
pub struct TemporalAntiAliasing {

View File

@ -10,7 +10,6 @@ use alloc::{
pub use bevy_derive::AppLabel;
use bevy_ecs::{
component::RequiredComponentsError,
error::{BevyError, SystemErrorContext},
event::{event_update_system, EventCursor},
intern::Interned,
prelude::*,
@ -1279,18 +1278,6 @@ impl App {
self
}
/// Set the global system error handler to use for systems that return a [`Result`].
///
/// See the [`bevy_ecs::result` module-level documentation](../../bevy_ecs/result/index.html)
/// for more information.
pub fn set_system_error_handler(
&mut self,
error_handler: fn(BevyError, SystemErrorContext),
) -> &mut Self {
self.main_mut().set_system_error_handler(error_handler);
self
}
/// Attempts to determine if an [`AppExit`] was raised since the last update.
///
/// Will attempt to return the first [`Error`](AppExit::Error) it encounters.

View File

@ -1,7 +1,6 @@
use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
use alloc::{boxed::Box, string::String, vec::Vec};
use bevy_ecs::{
error::{DefaultSystemErrorHandler, SystemErrorContext},
event::EventRegistry,
prelude::*,
schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
@ -336,22 +335,6 @@ impl SubApp {
self
}
/// Set the global error handler to use for systems that return a [`Result`].
///
/// See the [`bevy_ecs::result` module-level documentation](../../bevy_ecs/result/index.html)
/// for more information.
pub fn set_system_error_handler(
&mut self,
error_handler: fn(BevyError, SystemErrorContext),
) -> &mut Self {
let mut default_handler = self
.world_mut()
.get_resource_or_init::<DefaultSystemErrorHandler>();
default_handler.0 = error_handler;
self
}
/// See [`App::add_event`].
pub fn add_event<T>(&mut self) -> &mut Self
where

View File

@ -129,7 +129,7 @@ impl core::fmt::Debug for StrongHandle {
///
/// [`Handle::Strong`], via [`StrongHandle`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists).
#[derive(Reflect)]
#[reflect(Default, Debug, Hash, PartialEq)]
#[reflect(Default, Debug, Hash, PartialEq, Clone)]
pub enum Handle<A: Asset> {
/// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
/// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.
@ -658,7 +658,7 @@ mod tests {
assert_eq!(UntypedHandle::from(typed.clone()), untyped);
}
/// `Reflect::clone_value` should increase the strong count of a strong handle
/// `PartialReflect::reflect_clone`/`PartialReflect::to_dynamic` should increase the strong count of a strong handle
#[test]
fn strong_handle_reflect_clone() {
use crate::{AssetApp, AssetPlugin, Assets, VisitAssetDependencies};
@ -689,7 +689,7 @@ mod tests {
);
let reflected: &dyn Reflect = &handle;
let cloned_handle: Box<dyn PartialReflect> = reflected.clone_value();
let _cloned_handle: Box<dyn Reflect> = reflected.reflect_clone().unwrap();
assert_eq!(
Arc::strong_count(strong),
@ -697,10 +697,18 @@ mod tests {
"Cloning the handle with reflect should increase the strong count to 2"
);
let from_reflect_handle: Handle<MyAsset> =
FromReflect::from_reflect(&*cloned_handle).unwrap();
let dynamic_handle: Box<dyn PartialReflect> = reflected.to_dynamic();
assert_eq!(Arc::strong_count(strong), 3, "Converting the reflected value back to a handle should increase the strong count to 3");
assert_eq!(
Arc::strong_count(strong),
3,
"Converting the handle to a dynamic should increase the strong count to 3"
);
let from_reflect_handle: Handle<MyAsset> =
FromReflect::from_reflect(&*dynamic_handle).unwrap();
assert_eq!(Arc::strong_count(strong), 4, "Converting the reflected value back to a handle should increase the strong count to 4");
assert!(
from_reflect_handle.is_strong(),
"The cloned handle should still be strong"

View File

@ -1,5 +1,5 @@
use crate::{Asset, AssetIndex};
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -19,6 +19,7 @@ use thiserror::Error;
///
/// For an "untyped" / "generic-less" id, see [`UntypedAssetId`].
#[derive(Reflect, Serialize, Deserialize, From)]
#[reflect(Clone, Default, Debug, PartialEq, Hash)]
pub enum AssetId<A: Asset> {
/// A small / efficient runtime identifier that can be used to efficiently look up an asset stored in [`Assets`]. This is
/// the "default" identifier used for assets. The alternative(s) (ex: [`AssetId::Uuid`]) will only be used if assets are
@ -29,7 +30,7 @@ pub enum AssetId<A: Asset> {
/// The unstable, opaque index of the asset.
index: AssetIndex,
/// A marker to store the type information of the asset.
#[reflect(ignore)]
#[reflect(ignore, clone)]
marker: PhantomData<fn() -> A>,
},
/// A stable-across-runs / const asset identifier. This will only be used if an asset is explicitly registered in [`Assets`]

View File

@ -35,7 +35,7 @@ impl FileWatcher {
sender: Sender<AssetSourceEvent>,
debounce_wait_time: Duration,
) -> Result<Self, notify::Error> {
let root = normalize_path(&path);
let root = normalize_path(&path).canonicalize().unwrap();
let watcher = new_asset_event_debouncer(
path.clone(),
debounce_wait_time,

View File

@ -1823,6 +1823,83 @@ mod tests {
});
}
// This test is not checking a requirement, but documenting a current limitation. We simply are
// not capable of loading subassets when doing nested immediate loads.
#[test]
fn error_on_nested_immediate_load_of_subasset() {
let mut app = App::new();
let dir = Dir::default();
dir.insert_asset_text(
Path::new("a.cool.ron"),
r#"(
text: "b",
dependencies: [],
embedded_dependencies: [],
sub_texts: ["A"],
)"#,
);
dir.insert_asset_text(Path::new("empty.txt"), "");
app.register_asset_source(
AssetSourceId::Default,
AssetSource::build()
.with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })),
)
.add_plugins((
TaskPoolPlugin::default(),
LogPlugin::default(),
AssetPlugin::default(),
));
app.init_asset::<CoolText>()
.init_asset::<SubText>()
.register_asset_loader(CoolTextLoader);
struct NestedLoadOfSubassetLoader;
impl AssetLoader for NestedLoadOfSubassetLoader {
type Asset = TestAsset;
type Error = crate::loader::LoadDirectError;
type Settings = ();
async fn load(
&self,
_: &mut dyn Reader,
_: &Self::Settings,
load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
// We expect this load to fail.
load_context
.loader()
.immediate()
.load::<SubText>("a.cool.ron#A")
.await?;
Ok(TestAsset)
}
fn extensions(&self) -> &[&str] {
&["txt"]
}
}
app.init_asset::<TestAsset>()
.register_asset_loader(NestedLoadOfSubassetLoader);
let asset_server = app.world().resource::<AssetServer>().clone();
let handle = asset_server.load::<TestAsset>("empty.txt");
run_app_until(&mut app, |_world| match asset_server.load_state(&handle) {
LoadState::Loading => None,
LoadState::Failed(err) => {
let error_message = format!("{err}");
assert!(error_message.contains("Requested to load an asset path (a.cool.ron#A) with a subasset, but this is unsupported"), "what? \"{error_message}\"");
Some(())
}
state => panic!("Unexpected asset state: {state:?}"),
});
}
// validate the Asset derive macro for various asset types
#[derive(Asset, TypePath)]
pub struct TestAsset;

View File

@ -359,10 +359,14 @@ impl<A: Asset> From<CompleteLoadedAsset<A>> for CompleteErasedLoadedAsset {
/// [`NestedLoader::load`]: crate::NestedLoader::load
/// [immediately]: crate::Immediate
#[derive(Error, Debug)]
#[error("Failed to load dependency {dependency:?} {error}")]
pub struct LoadDirectError {
pub dependency: AssetPath<'static>,
pub error: AssetLoadError,
pub enum LoadDirectError {
#[error("Requested to load an asset path ({0:?}) with a subasset, but this is unsupported. See issue #18291")]
RequestedSubasset(AssetPath<'static>),
#[error("Failed to load dependency {dependency:?} {error}")]
LoadError {
dependency: AssetPath<'static>,
error: AssetLoadError,
},
}
/// An error that occurs while deserializing [`AssetMeta`].
@ -621,7 +625,7 @@ impl<'a> LoadContext<'a> {
self.populate_hashes,
)
.await
.map_err(|error| LoadDirectError {
.map_err(|error| LoadDirectError::LoadError {
dependency: path.clone(),
error,
})?;

View File

@ -387,13 +387,16 @@ impl<'builder, 'reader, T> NestedLoader<'_, '_, T, Immediate<'builder, 'reader>>
path: &AssetPath<'static>,
asset_type_id: Option<TypeId>,
) -> Result<(Arc<dyn ErasedAssetLoader>, CompleteErasedLoadedAsset), LoadDirectError> {
if path.label().is_some() {
return Err(LoadDirectError::RequestedSubasset(path.clone()));
}
let (mut meta, loader, mut reader) = if let Some(reader) = self.mode.reader {
let loader = if let Some(asset_type_id) = asset_type_id {
self.load_context
.asset_server
.get_asset_loader_with_asset_type_id(asset_type_id)
.await
.map_err(|error| LoadDirectError {
.map_err(|error| LoadDirectError::LoadError {
dependency: path.clone(),
error: error.into(),
})?
@ -402,7 +405,7 @@ impl<'builder, 'reader, T> NestedLoader<'_, '_, T, Immediate<'builder, 'reader>>
.asset_server
.get_path_asset_loader(path)
.await
.map_err(|error| LoadDirectError {
.map_err(|error| LoadDirectError::LoadError {
dependency: path.clone(),
error: error.into(),
})?
@ -415,7 +418,7 @@ impl<'builder, 'reader, T> NestedLoader<'_, '_, T, Immediate<'builder, 'reader>>
.asset_server
.get_meta_loader_and_reader(path, asset_type_id)
.await
.map_err(|error| LoadDirectError {
.map_err(|error| LoadDirectError::LoadError {
dependency: path.clone(),
error,
})?;
@ -453,15 +456,17 @@ impl NestedLoader<'_, '_, StaticTyped, Immediate<'_, '_>> {
self.load_internal(&path, Some(TypeId::of::<A>()))
.await
.and_then(move |(loader, untyped_asset)| {
untyped_asset.downcast::<A>().map_err(|_| LoadDirectError {
dependency: path.clone(),
error: AssetLoadError::RequestedHandleTypeMismatch {
path,
requested: TypeId::of::<A>(),
actual_asset_name: loader.asset_type_name(),
loader_name: loader.type_name(),
},
})
untyped_asset
.downcast::<A>()
.map_err(|_| LoadDirectError::LoadError {
dependency: path.clone(),
error: AssetLoadError::RequestedHandleTypeMismatch {
path,
requested: TypeId::of::<A>(),
actual_asset_name: loader.asset_type_name(),
loader_name: loader.type_name(),
},
})
})
}
}

View File

@ -53,7 +53,7 @@ use thiserror::Error;
/// This also means that you should use [`AssetPath::parse`] in cases where `&str` is the explicit type.
#[derive(Eq, PartialEq, Hash, Clone, Default, Reflect)]
#[reflect(opaque)]
#[reflect(Debug, PartialEq, Hash, Serialize, Deserialize)]
#[reflect(Debug, PartialEq, Hash, Clone, Serialize, Deserialize)]
pub struct AssetPath<'a> {
source: AssetSourceId<'a>,
path: CowArc<'a, Path>,

View File

@ -27,7 +27,7 @@ bitflags::bitflags! {
#[repr(transparent)]
#[derive(Serialize, Deserialize, Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
#[reflect(opaque)]
#[reflect(Serialize, Deserialize, Hash, PartialEq, Debug)]
#[reflect(Serialize, Deserialize, Hash, Clone, PartialEq, Debug)]
pub struct RenderAssetUsages: u8 {
/// The bit flag for the main world.
const MAIN_WORLD = 1 << 0;

View File

@ -6,6 +6,7 @@ use bevy_reflect::prelude::*;
/// The way Bevy manages the sound playback.
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone)]
pub enum PlaybackMode {
/// Play the sound once. Do nothing when it ends.
///
@ -29,7 +30,7 @@ pub enum PlaybackMode {
/// [`AudioSink`][crate::AudioSink] or [`SpatialAudioSink`][crate::SpatialAudioSink]
/// components. Changes to this component will *not* be applied to already-playing audio.
#[derive(Component, Clone, Copy, Debug, Reflect)]
#[reflect(Default, Component, Debug)]
#[reflect(Clone, Default, Component, Debug)]
pub struct PlaybackSettings {
/// The desired playback behavior.
pub mode: PlaybackMode,
@ -142,7 +143,7 @@ impl PlaybackSettings {
/// This must be accompanied by `Transform` and `GlobalTransform`.
/// Only one entity with a `SpatialListener` should be present at any given time.
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Default, Component, Debug)]
#[reflect(Clone, Default, Component, Debug)]
pub struct SpatialListener {
/// Left ear position relative to the `GlobalTransform`.
pub left_ear_offset: Vec3,
@ -174,6 +175,7 @@ impl SpatialListener {
///
/// Default is `Vec3::ONE`.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Clone, Default)]
pub struct SpatialScale(pub Vec3);
impl SpatialScale {
@ -202,7 +204,7 @@ impl Default for SpatialScale {
///
/// Default is `Vec3::ONE`.
#[derive(Resource, Default, Clone, Copy, Reflect)]
#[reflect(Resource, Default)]
#[reflect(Resource, Default, Clone)]
pub struct DefaultSpatialScale(pub SpatialScale);
/// A component for playing a sound.
@ -218,7 +220,7 @@ pub struct DefaultSpatialScale(pub SpatialScale);
/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the
/// `PlaybackSettings` component will *not* affect already-playing audio.
#[derive(Component, Reflect)]
#[reflect(Component)]
#[reflect(Component, Clone)]
#[require(PlaybackSettings)]
pub struct AudioPlayer<Source = AudioSource>(pub Handle<Source>)
where

View File

@ -6,7 +6,7 @@ use bevy_reflect::prelude::*;
///
/// Note: Changing [`GlobalVolume`] does not affect already playing audio.
#[derive(Resource, Debug, Default, Clone, Copy, Reflect)]
#[reflect(Resource, Debug, Default)]
#[reflect(Resource, Debug, Default, Clone)]
pub struct GlobalVolume {
/// The global volume of all audio.
pub volume: Volume,
@ -32,7 +32,7 @@ impl GlobalVolume {
///
/// To create a new [`Volume`] from decibels, use [`Volume::Decibels`].
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Debug, PartialEq)]
#[reflect(Clone, Debug, PartialEq)]
pub enum Volume {
/// Create a new [`Volume`] from the given volume in linear scale.
///

View File

@ -42,7 +42,11 @@ use derive_more::derive::From;
/// To avoid the cost of repeated conversion, and ensure consistent results where that is desired,
/// first convert this [`Color`] into your desired color space.
#[derive(Debug, Clone, Copy, PartialEq, From)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -13,7 +13,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -13,7 +13,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -16,7 +16,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -12,7 +12,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -12,7 +12,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -13,7 +13,11 @@ use bytemuck::{Pod, Zeroable};
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -12,7 +12,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -12,7 +12,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -15,7 +15,11 @@ use thiserror::Error;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -12,7 +12,11 @@ use bevy_reflect::prelude::*;
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(PartialEq, Default))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Default)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -18,7 +18,6 @@ trace = []
webgl = []
webgpu = []
tonemapping_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
[dependencies]
# bevy

View File

@ -18,7 +18,7 @@ const LUT_SIZE: usize = 256;
/// This curve is used to map the average log luminance of a scene to an
/// exposure compensation value, to allow for fine control over the final exposure.
#[derive(Asset, Reflect, Debug, Clone)]
#[reflect(Default)]
#[reflect(Default, Clone)]
pub struct AutoExposureCompensationCurve {
/// The minimum log luminance value in the curve. (the x-axis)
min_log_lum: f32,

View File

@ -24,7 +24,7 @@ use bevy_utils::default;
///
/// **Auto Exposure requires compute shaders and is not compatible with WebGL2.**
#[derive(Component, Clone, Reflect, ExtractComponent)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct AutoExposure {
/// The range of exposure values for the histogram.
///

View File

@ -25,7 +25,7 @@ use bevy_render::{extract_component::ExtractComponent, prelude::Camera};
/// See <https://starlederer.github.io/bloom/> for a visualization of the parametric curve
/// used in Bevy as well as a visualization of the curve's respective scattering profile.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct Bloom {
/// Controls the baseline of how much the image is scattered (default: 0.15).
///
@ -193,6 +193,7 @@ impl Default for Bloom {
/// * Changing these settings makes it easy to make the final result look worse
/// * Non-default prefilter settings should be used in conjunction with [`BloomCompositeMode::Additive`]
#[derive(Default, Clone, Reflect)]
#[reflect(Clone, Default)]
pub struct BloomPrefilter {
/// Baseline of the quadratic threshold curve (default: 0.0).
///
@ -209,6 +210,7 @@ pub struct BloomPrefilter {
}
#[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, Copy)]
#[reflect(Clone, Hash, PartialEq)]
pub enum BloomCompositeMode {
EnergyConserving,
Additive,

View File

@ -14,7 +14,7 @@ use bevy_transform::prelude::{GlobalTransform, Transform};
/// A 2D camera component. Enables the 2D render graph for a [`Camera`].
#[derive(Component, Default, Reflect, Clone, ExtractComponent)]
#[extract_component_filter(With<Camera>)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[require(
Camera,
DebandDither,

View File

@ -44,6 +44,15 @@ impl ViewNode for MainTransparentPass2dNode {
let diagnostics = render_context.diagnostic_recorder();
let color_attachments = [Some(target.get_color_attachment())];
// NOTE: For the transparent pass we load the depth buffer. There should be no
// need to write to it, but store is set to `true` as a workaround for issue #3776,
// https://github.com/bevyengine/bevy/issues/3776
// so that wgpu does not clear the depth buffer.
// As the opaque and alpha mask passes run first, opaque meshes can occlude
// transparent ones.
let depth_stencil_attachment = Some(depth.get_attachment(StoreOp::Store));
render_context.add_command_buffer_generation_task(move |render_device| {
// Command encoder setup
let mut command_encoder =
@ -58,14 +67,8 @@ impl ViewNode for MainTransparentPass2dNode {
let render_pass = command_encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("main_transparent_pass_2d"),
color_attachments: &[Some(target.get_color_attachment())],
// NOTE: For the transparent pass we load the depth buffer. There should be no
// need to write to it, but store is set to `true` as a workaround for issue #3776,
// https://github.com/bevyengine/bevy/issues/3776
// so that wgpu does not clear the depth buffer.
// As the opaque and alpha mask passes run first, opaque meshes can occlude
// transparent ones.
depth_stencil_attachment: Some(depth.get_attachment(StoreOp::Store)),
color_attachments: &color_attachments,
depth_stencil_attachment,
timestamp_writes: None,
occlusion_query_set: None,
});

View File

@ -349,6 +349,7 @@ pub struct Transparent2d {
pub pipeline: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub extracted_index: usize,
pub extra_index: PhaseItemExtraIndex,
/// Whether the mesh in question is indexed (uses an index buffer in
/// addition to its vertex buffer).

View File

@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize};
/// This means "forward" is -Z.
#[derive(Component, Reflect, Clone, ExtractComponent)]
#[extract_component_filter(With<Camera>)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[require(
Camera,
DebandDither(|| DebandDither::Enabled),
@ -56,7 +56,7 @@ pub struct Camera3d {
///
/// Higher qualities are more GPU-intensive.
///
/// **Note:** You can get better-looking results at any quality level by enabling TAA. See: [`TemporalAntiAliasPlugin`](crate::experimental::taa::TemporalAntiAliasPlugin).
/// **Note:** You can get better-looking results at any quality level by enabling TAA. See: `TemporalAntiAliasPlugin`
pub screen_space_specular_transmission_quality: ScreenSpaceTransmissionQuality,
}
@ -72,7 +72,7 @@ impl Default for Camera3d {
}
#[derive(Clone, Copy, Reflect, Serialize, Deserialize)]
#[reflect(Serialize, Deserialize)]
#[reflect(Serialize, Deserialize, Clone)]
pub struct Camera3dDepthTextureUsage(pub u32);
impl From<TextureUsages> for Camera3dDepthTextureUsage {
@ -88,7 +88,7 @@ impl From<Camera3dDepthTextureUsage> for TextureUsages {
/// The depth clear operation to perform for the main 3d pass.
#[derive(Reflect, Serialize, Deserialize, Clone, Debug)]
#[reflect(Serialize, Deserialize)]
#[reflect(Serialize, Deserialize, Clone, Default)]
pub enum Camera3dDepthLoadOp {
/// Clear with a specified value.
/// Note that 0.0 is the far plane due to bevy's use of reverse-z projections.
@ -117,9 +117,9 @@ impl From<Camera3dDepthLoadOp> for LoadOp<f32> {
///
/// Higher qualities are more GPU-intensive.
///
/// **Note:** You can get better-looking results at any quality level by enabling TAA. See: [`TemporalAntiAliasPlugin`](crate::experimental::taa::TemporalAntiAliasPlugin).
/// **Note:** You can get better-looking results at any quality level by enabling TAA. See: `TemporalAntiAliasPlugin`
#[derive(Resource, Default, Clone, Copy, Reflect, PartialEq, PartialOrd, Debug)]
#[reflect(Resource, Default, Debug, PartialEq)]
#[reflect(Resource, Default, Clone, Debug, PartialEq)]
pub enum ScreenSpaceTransmissionQuality {
/// Best performance at the cost of quality. Suitable for lower end GPUs. (e.g. Mobile)
///

View File

@ -79,7 +79,7 @@ pub struct DepthOfFieldPlugin;
///
/// [depth of field]: https://en.wikipedia.org/wiki/Depth_of_field
#[derive(Component, Clone, Copy, Reflect)]
#[reflect(Component, Default)]
#[reflect(Component, Clone, Default)]
pub struct DepthOfField {
/// The appearance of the effect.
pub mode: DepthOfFieldMode,
@ -123,7 +123,7 @@ pub struct DepthOfField {
/// Controls the appearance of the effect.
#[derive(Clone, Copy, Default, PartialEq, Debug, Reflect)]
#[reflect(Default, PartialEq)]
#[reflect(Default, Clone, PartialEq)]
pub enum DepthOfFieldMode {
/// A more accurate simulation, in which circles of confusion generate
/// "spots" of light.

View File

@ -5,7 +5,3 @@
//! are included nonetheless for testing purposes.
pub mod mip_generation;
pub mod taa {
pub use crate::taa::{TemporalAntiAliasNode, TemporalAntiAliasPlugin, TemporalAntiAliasing};
}

View File

@ -9,22 +9,18 @@
pub mod auto_exposure;
pub mod blit;
pub mod bloom;
pub mod contrast_adaptive_sharpening;
pub mod core_2d;
pub mod core_3d;
pub mod deferred;
pub mod dof;
pub mod experimental;
pub mod fullscreen_vertex_shader;
pub mod fxaa;
pub mod motion_blur;
pub mod msaa_writeback;
pub mod oit;
pub mod post_process;
pub mod prepass;
mod skybox;
pub mod smaa;
mod taa;
pub mod tonemapping;
pub mod upscaling;
@ -41,19 +37,16 @@ pub mod prelude {
use crate::{
blit::BlitPlugin,
bloom::BloomPlugin,
contrast_adaptive_sharpening::CasPlugin,
core_2d::Core2dPlugin,
core_3d::Core3dPlugin,
deferred::copy_lighting_id::CopyDeferredLightingIdPlugin,
dof::DepthOfFieldPlugin,
experimental::mip_generation::MipGenerationPlugin,
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
fxaa::FxaaPlugin,
motion_blur::MotionBlurPlugin,
msaa_writeback::MsaaWritebackPlugin,
post_process::PostProcessingPlugin,
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
smaa::SmaaPlugin,
tonemapping::TonemappingPlugin,
upscaling::UpscalingPlugin,
};
@ -85,11 +78,8 @@ impl Plugin for CorePipelinePlugin {
TonemappingPlugin,
UpscalingPlugin,
BloomPlugin,
FxaaPlugin,
CasPlugin,
MotionBlurPlugin,
DepthOfFieldPlugin,
SmaaPlugin,
PostProcessingPlugin,
OrderIndependentTransparencyPlugin,
MipGenerationPlugin,

View File

@ -54,7 +54,7 @@ pub mod pipeline;
/// # }
/// ````
#[derive(Reflect, Component, Clone, ExtractComponent, ShaderType)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
#[extract_component_filter(With<Camera>)]
#[require(DepthPrepass, MotionVectorPrepass)]
pub struct MotionBlur {

View File

@ -6,7 +6,7 @@ use bevy_ecs::{component::*, prelude::*};
use bevy_math::UVec2;
use bevy_platform_support::collections::HashSet;
use bevy_platform_support::time::Instant;
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
camera::{Camera, ExtractedCamera},
extract_component::{ExtractComponent, ExtractComponentPlugin},
@ -44,6 +44,7 @@ pub const OIT_DRAW_SHADER_HANDLE: Handle<Shader> =
// This should probably be done by adding an enum to this component.
// We use the same struct to pass on the settings to the drawing shader.
#[derive(Clone, Copy, ExtractComponent, Reflect, ShaderType)]
#[reflect(Clone, Default)]
pub struct OrderIndependentTransparencySettings {
/// Controls how many layers will be used to compute the blending.
/// The more layers you use the more memory it will use but it will also give better results.

View File

@ -98,7 +98,7 @@ pub struct PostProcessingPlugin;
///
/// [Gjøl & Svendsen 2016]: https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf
#[derive(Reflect, Component, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct ChromaticAberration {
/// The lookup texture that determines the color gradient.
///

View File

@ -54,18 +54,18 @@ pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float
/// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass.
#[derive(Component, Default, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct DepthPrepass;
/// If added to a [`crate::prelude::Camera3d`] then vertex world normals will be copied to a separate texture available to the main pass.
/// Normals will have normal map textures already applied.
#[derive(Component, Default, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct NormalPrepass;
/// If added to a [`crate::prelude::Camera3d`] then screen space motion vectors will be copied to a separate texture available to the main pass.
#[derive(Component, Default, Reflect, Clone)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct MotionVectorPrepass;
/// If added to a [`crate::prelude::Camera3d`] then deferred materials will be rendered to the deferred gbuffer texture and will be available to subsequent passes.

View File

@ -90,7 +90,7 @@ impl Plugin for SkyboxPlugin {
///
/// See also <https://en.wikipedia.org/wiki/Skybox_(video_games)>.
#[derive(Component, Clone, Reflect)]
#[reflect(Component, Default)]
#[reflect(Component, Default, Clone)]
pub struct Skybox {
pub image: Handle<Image>,
/// Scale factor applied to the skybox image.

View File

@ -20,15 +20,6 @@ pub fn bevy_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
main();
}
// SAFETY: `#[bevy_main]` should only be placed on a single `main` function
// TODO: Potentially make `bevy_main` and unsafe attribute as there is a safety
// guarantee required from the caller.
#[unsafe(no_mangle)]
#[cfg(target_os = "ios")]
extern "C" fn main_rs() {
main();
}
#[allow(unused)]
#input
})

View File

@ -29,9 +29,13 @@ impl Plugin for SystemInformationDiagnosticsPlugin {
impl SystemInformationDiagnosticsPlugin {
/// Total system cpu usage in %
pub const CPU_USAGE: DiagnosticPath = DiagnosticPath::const_new("system/cpu_usage");
pub const SYSTEM_CPU_USAGE: DiagnosticPath = DiagnosticPath::const_new("system/cpu_usage");
/// Total system memory usage in %
pub const MEM_USAGE: DiagnosticPath = DiagnosticPath::const_new("system/mem_usage");
pub const SYSTEM_MEM_USAGE: DiagnosticPath = DiagnosticPath::const_new("system/mem_usage");
/// Process cpu usage in %
pub const PROCESS_CPU_USAGE: DiagnosticPath = DiagnosticPath::const_new("process/cpu_usage");
/// Process memory usage in %
pub const PROCESS_MEM_USAGE: DiagnosticPath = DiagnosticPath::const_new("process/mem_usage");
}
/// A resource that stores diagnostic information about the system.
@ -90,15 +94,26 @@ pub mod internal {
}
fn setup_system(mut diagnostics: ResMut<DiagnosticsStore>) {
diagnostics
.add(Diagnostic::new(SystemInformationDiagnosticsPlugin::CPU_USAGE).with_suffix("%"));
diagnostics
.add(Diagnostic::new(SystemInformationDiagnosticsPlugin::MEM_USAGE).with_suffix("%"));
diagnostics.add(
Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE).with_suffix("%"),
);
diagnostics.add(
Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE).with_suffix("%"),
);
diagnostics.add(
Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE).with_suffix("%"),
);
diagnostics.add(
Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE)
.with_suffix("GiB"),
);
}
struct SysinfoRefreshData {
current_cpu_usage: f64,
current_used_mem: f64,
system_cpu_usage: f64,
system_mem_usage: f64,
process_cpu_usage: f64,
process_mem_usage: f64,
}
#[derive(Resource, Default)]
@ -135,18 +150,31 @@ pub mod internal {
let sys = Arc::clone(sysinfo);
let task = thread_pool.spawn(async move {
let mut sys = sys.lock().unwrap();
let pid = sysinfo::get_current_pid().expect("Failed to get current process ID");
sys.refresh_processes(sysinfo::ProcessesToUpdate::Some(&[pid]), true);
sys.refresh_cpu_specifics(CpuRefreshKind::nothing().with_cpu_usage());
sys.refresh_memory();
let current_cpu_usage = sys.global_cpu_usage().into();
// `memory()` fns return a value in bytes
let total_mem = sys.total_memory() as f64 / BYTES_TO_GIB;
let used_mem = sys.used_memory() as f64 / BYTES_TO_GIB;
let current_used_mem = used_mem / total_mem * 100.0;
let system_cpu_usage = sys.global_cpu_usage().into();
let total_mem = sys.total_memory() as f64;
let used_mem = sys.used_memory() as f64;
let system_mem_usage = used_mem / total_mem * 100.0;
let process_mem_usage = sys
.process(pid)
.map(|p| p.memory() as f64 * BYTES_TO_GIB)
.unwrap_or(0.0);
let process_cpu_usage = sys
.process(pid)
.map(|p| p.cpu_usage() as f64 / sys.cpus().len() as f64)
.unwrap_or(0.0);
SysinfoRefreshData {
current_cpu_usage,
current_used_mem,
system_cpu_usage,
system_mem_usage,
process_cpu_usage,
process_mem_usage,
}
});
tasks.tasks.push(task);
@ -160,12 +188,22 @@ pub mod internal {
return true;
};
diagnostics.add_measurement(&SystemInformationDiagnosticsPlugin::CPU_USAGE, || {
data.current_cpu_usage
});
diagnostics.add_measurement(&SystemInformationDiagnosticsPlugin::MEM_USAGE, || {
data.current_used_mem
});
diagnostics.add_measurement(
&SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE,
|| data.system_cpu_usage,
);
diagnostics.add_measurement(
&SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE,
|| data.system_mem_usage,
);
diagnostics.add_measurement(
&SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE,
|| data.process_cpu_usage,
);
diagnostics.add_measurement(
&SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE,
|| data.process_mem_usage,
);
false
});
}

View File

@ -12,7 +12,11 @@ keywords = ["bevy"]
crate-type = ["dylib"]
[dependencies]
bevy_internal = { path = "../bevy_internal", version = "0.16.0-dev", default-features = false }
# feature std is needed to avoid an issue when linking critical_section
# bevy_dylib is not expected to work in no_std
bevy_internal = { path = "../bevy_internal", version = "0.16.0-dev", default-features = false, features = [
"std",
] }
[lints]
workspace = true

View File

@ -34,7 +34,11 @@ bevy_reflect = ["dep:bevy_reflect"]
reflect_functions = ["bevy_reflect", "bevy_reflect/functions"]
reflect_auto_register = ["bevy_reflect", "bevy_reflect/auto_register"]
## Use the configurable global error handler as the default error handler
## Use the configurable global error handler as the default error handler.
##
## This is typically used to turn panics from the ECS into loggable errors.
## This may be useful for production builds,
## but can result in a measurable performance impact, especially for commands.
configurable_error_handler = []
## Enables automatic backtrace capturing in BevyError

View File

@ -18,7 +18,7 @@ use crate::{
observer::Observers,
prelude::World,
query::DebugCheckedUnwrap,
relationship::RelationshipInsertHookMode,
relationship::RelationshipHookMode,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, EntityWorldMut, ON_ADD, ON_INSERT, ON_REPLACE},
};
@ -1104,7 +1104,7 @@ impl<'w> BundleInserter<'w> {
bundle: T,
insert_mode: InsertMode,
caller: MaybeLocation,
relationship_insert_hook_mode: RelationshipInsertHookMode,
relationship_hook_mode: RelationshipHookMode,
) -> (EntityLocation, T::Effect) {
let bundle_info = self.bundle_info.as_ref();
let archetype_after_insert = self.archetype_after_insert.as_ref();
@ -1130,6 +1130,7 @@ impl<'w> BundleInserter<'w> {
entity,
archetype_after_insert.iter_existing(),
caller,
relationship_hook_mode,
);
}
}
@ -1317,7 +1318,7 @@ impl<'w> BundleInserter<'w> {
entity,
archetype_after_insert.iter_inserted(),
caller,
relationship_insert_hook_mode,
relationship_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
@ -1336,7 +1337,7 @@ impl<'w> BundleInserter<'w> {
entity,
archetype_after_insert.iter_added(),
caller,
relationship_insert_hook_mode,
relationship_hook_mode,
);
if new_archetype.has_insert_observer() {
deferred_world.trigger_observers(
@ -1484,7 +1485,7 @@ impl<'w> BundleSpawner<'w> {
entity,
bundle_info.iter_contributed_components(),
caller,
RelationshipInsertHookMode::Run,
RelationshipHookMode::Run,
);
if archetype.has_insert_observer() {
deferred_world.trigger_observers(

View File

@ -1197,7 +1197,7 @@ impl<'w, T> From<Mut<'w, T>> for MutUntyped<'w> {
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MaybeLocation<T: ?Sized = &'static Location<'static>> {
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
#[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
marker: PhantomData<T>,
#[cfg(feature = "track_location")]
value: T,

View File

@ -6,7 +6,7 @@ use crate::{
change_detection::{MaybeLocation, MAX_CHANGE_AGE},
entity::{ComponentCloneCtx, Entity, SourceComponent},
query::DebugCheckedUnwrap,
relationship::RelationshipInsertHookMode,
relationship::RelationshipHookMode,
resource::Resource,
storage::{SparseSetIndex, SparseSets, Table, TableRow},
system::{Commands, Local, SystemParam},
@ -584,7 +584,7 @@ pub struct HookContext {
/// The caller location is `Some` if the `track_caller` feature is enabled.
pub caller: MaybeLocation,
/// Configures how relationship hooks will run
pub relationship_insert_hook_mode: RelationshipInsertHookMode,
pub relationship_hook_mode: RelationshipHookMode,
}
/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
@ -950,7 +950,7 @@ impl ComponentInfo {
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq)
reflect(Debug, Hash, PartialEq, Clone)
)]
pub struct ComponentId(usize);
@ -2416,7 +2416,7 @@ impl Components {
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq)
reflect(Debug, Hash, PartialEq, Clone)
)]
pub struct Tick {
tick: u32,
@ -2513,7 +2513,7 @@ impl<'a> TickCells<'a> {
/// Records when a component or resource was added and when it was last mutably dereferenced (or added).
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))]
pub struct ComponentTicks {
/// Tick recording the time this component or resource was added.
pub added: Tick,
@ -2911,12 +2911,14 @@ pub fn component_clone_via_clone<C: Clone + Component>(
/// - Component has [`TypeId`]
/// - Component is registered
/// - Component has [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) registered
/// - Component has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect),
/// - Component can be cloned via [`PartialReflect::reflect_clone`] _or_ has one of the following registered: [`ReflectFromReflect`](bevy_reflect::ReflectFromReflect),
/// [`ReflectDefault`](bevy_reflect::std_traits::ReflectDefault), [`ReflectFromWorld`](crate::reflect::ReflectFromWorld)
///
/// If any of the conditions is not satisfied, the component will be skipped.
///
/// See [`EntityClonerBuilder`](crate::entity::EntityClonerBuilder) for details.
///
/// [`PartialReflect::reflect_clone`]: bevy_reflect::PartialReflect::reflect_clone
#[cfg(feature = "bevy_reflect")]
pub fn component_clone_via_reflect(
commands: &mut Commands,
@ -2934,6 +2936,21 @@ pub fn component_clone_via_reflect(
// checked in read_source_component_reflect
let type_id = component_info.type_id().unwrap();
// Try to clone using `reflect_clone`
if let Ok(mut component) = source_component_reflect.reflect_clone() {
if let Some(reflect_component) =
registry.get_type_data::<crate::reflect::ReflectComponent>(type_id)
{
reflect_component.visit_entities_mut(&mut *component, &mut |entity| {
*entity = ctx.entity_mapper().get_mapped(*entity);
});
}
drop(registry);
ctx.write_target_component_reflect(component);
return;
}
// Try to clone using ReflectFromReflect
if let Some(reflect_from_reflect) =
registry.get_type_data::<bevy_reflect::ReflectFromReflect>(type_id)
@ -2977,7 +2994,7 @@ pub fn component_clone_via_reflect(
mapped_entities.push(entity);
});
}
let source_component_cloned = source_component_reflect.clone_value();
let source_component_cloned = source_component_reflect.to_dynamic();
let component_layout = component_info.layout();
let target = ctx.target();
let component_id = ctx.component_id();
@ -3008,7 +3025,11 @@ pub fn component_clone_via_reflect(
world
.entity_mut(target)
.insert_by_id(component_id, OwningPtr::new(raw_component_ptr));
alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout);
if component_layout.size() > 0 {
// Ensure we don't attempt to deallocate zero-sized components
alloc::alloc::dealloc(raw_component_ptr.as_ptr(), component_layout);
}
}
});
}

View File

@ -10,7 +10,7 @@ use alloc::boxed::Box;
use crate::component::{ComponentCloneBehavior, ComponentCloneFn};
use crate::entity::hash_map::EntityHashMap;
use crate::entity::{Entities, EntityMapper};
use crate::relationship::RelationshipInsertHookMode;
use crate::relationship::RelationshipHookMode;
use crate::system::Commands;
use crate::{
bundle::Bundle,
@ -256,7 +256,11 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
);
self.bundle_scratch
.push_ptr(self.component_id, PtrMut::new(target_component_data_ptr));
alloc::alloc::dealloc(component_data_ptr, component_layout);
if component_layout.size() > 0 {
// Ensure we don't attempt to deallocate zero-sized components
alloc::alloc::dealloc(component_data_ptr, component_layout);
}
}
self.target_component_written = true;
@ -411,7 +415,7 @@ impl<'a> BundleScratch<'a> {
self,
world: &mut World,
entity: Entity,
relationship_hook_insert_mode: RelationshipInsertHookMode,
relationship_hook_insert_mode: RelationshipHookMode,
) {
// SAFETY:
// - All `component_ids` are from the same world as `target` entity
@ -449,7 +453,7 @@ impl EntityCloner {
world: &mut World,
source: Entity,
mapper: &mut dyn EntityMapper,
relationship_hook_insert_mode: RelationshipInsertHookMode,
relationship_hook_insert_mode: RelationshipHookMode,
) -> Entity {
let target = mapper.get_mapped(source);
// PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`.
@ -577,16 +581,15 @@ impl EntityCloner {
mapper: &mut dyn EntityMapper,
) -> Entity {
// All relationships on the root should have their hooks run
let target =
self.clone_entity_internal(world, source, mapper, RelationshipInsertHookMode::Run);
let target = self.clone_entity_internal(world, source, mapper, RelationshipHookMode::Run);
let child_hook_insert_mode = if self.linked_cloning {
// When spawning "linked relationships", we want to ignore hooks for relationships we are spawning, while
// still registering with original relationship targets that are "not linked" to the current recursive spawn.
RelationshipInsertHookMode::RunIfNotLinked
RelationshipHookMode::RunIfNotLinked
} else {
// If we are not cloning "linked relationships" recursively, then we want any cloned relationship components to
// register themselves with their original relationship target.
RelationshipInsertHookMode::Run
RelationshipHookMode::Run
};
loop {
let queued = self.clone_queue.pop_front();
@ -848,6 +851,7 @@ mod tests {
use alloc::vec::Vec;
use bevy_ptr::OwningPtr;
use bevy_reflect::Reflect;
use core::marker::PhantomData;
use core::{alloc::Layout, ops::Deref};
#[cfg(feature = "bevy_reflect")]
@ -888,67 +892,95 @@ mod tests {
assert!(world.get::<A>(e_clone).is_some_and(|c| *c == component));
}
// TODO: remove this when https://github.com/bevyengine/bevy/pull/13432 lands
#[test]
fn clone_entity_using_reflect_all_paths() {
// `ReflectDefault`-based fast path
#[derive(PartialEq, Eq, Default, Debug)]
struct NotClone;
// `reflect_clone`-based fast path
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
#[reflect(Default)]
#[reflect(from_reflect = false)]
struct A {
field: usize,
field2: Vec<usize>,
}
// `ReflectFromReflect`-based fast path
// `ReflectDefault`-based fast path
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
#[reflect(Default)]
#[reflect(from_reflect = false)]
struct B {
field: usize,
field2: Vec<usize>,
#[reflect(ignore)]
ignored: NotClone,
}
// `ReflectFromReflect`-based fast path
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
struct C {
field: usize,
field2: Vec<usize>,
#[reflect(ignore)]
ignored: NotClone,
}
// `ReflectFromWorld`-based fast path
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
#[reflect(FromWorld)]
#[reflect(from_reflect = false)]
struct C {
struct D {
field: usize,
field2: Vec<usize>,
#[reflect(ignore)]
ignored: NotClone,
}
let mut world = World::default();
world.init_resource::<AppTypeRegistry>();
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
registry.write().register::<(A, B, C)>();
registry.write().register::<(A, B, C, D)>();
let a_id = world.register_component::<A>();
let b_id = world.register_component::<B>();
let c_id = world.register_component::<C>();
let d_id = world.register_component::<D>();
let component_a = A {
field: 5,
field2: vec![1, 2, 3, 4, 5],
};
let component_b = B {
field: 6,
field: 5,
field2: vec![1, 2, 3, 4, 5],
ignored: NotClone,
};
let component_c = C {
field: 6,
field2: vec![1, 2, 3, 4, 5],
ignored: NotClone,
};
let component_d = D {
field: 7,
field2: vec![1, 2, 3, 4, 5],
ignored: NotClone,
};
let e = world.spawn((component_a, component_b, component_c)).id();
let e = world
.spawn((component_a, component_b, component_c, component_d))
.id();
let e_clone = world.spawn_empty().id();
EntityCloner::build(&mut world)
.override_clone_behavior_with_id(a_id, ComponentCloneBehavior::reflect())
.override_clone_behavior_with_id(b_id, ComponentCloneBehavior::reflect())
.override_clone_behavior_with_id(c_id, ComponentCloneBehavior::reflect())
.override_clone_behavior_with_id(d_id, ComponentCloneBehavior::reflect())
.clone_entity(e, e_clone);
assert_eq!(world.get::<A>(e_clone), Some(world.get::<A>(e).unwrap()));
assert_eq!(world.get::<B>(e_clone), Some(world.get::<B>(e).unwrap()));
assert_eq!(world.get::<C>(e_clone), Some(world.get::<C>(e).unwrap()));
assert_eq!(world.get::<D>(e_clone), Some(world.get::<D>(e).unwrap()));
}
#[test]
@ -1025,16 +1057,16 @@ mod tests {
#[derive(Component, PartialEq, Eq, Default, Debug)]
struct A;
// No valid type data
// No valid type data and not `reflect_clone`-able
#[derive(Component, Reflect, PartialEq, Eq, Default, Debug)]
#[reflect(Component)]
#[reflect(from_reflect = false)]
struct B;
struct B(#[reflect(ignore)] PhantomData<()>);
let mut world = World::default();
// No AppTypeRegistry
let e = world.spawn((A, B)).id();
let e = world.spawn((A, B(Default::default()))).id();
let e_clone = world.spawn_empty().id();
EntityCloner::build(&mut world)
.override_clone_behavior::<A>(ComponentCloneBehavior::reflect())
@ -1048,7 +1080,7 @@ mod tests {
let registry = world.get_resource::<AppTypeRegistry>().unwrap();
registry.write().register::<B>();
let e = world.spawn((A, B)).id();
let e = world.spawn((A, B(Default::default()))).id();
let e_clone = world.spawn_empty().id();
EntityCloner::build(&mut world).clone_entity(e, e_clone);
assert_eq!(world.get::<A>(e_clone), None);
@ -1347,7 +1379,11 @@ mod tests {
fn clone_with_reflect_from_world() {
#[derive(Component, Reflect, PartialEq, Eq, Debug)]
#[reflect(Component, FromWorld, from_reflect = false)]
struct SomeRef(#[entities] Entity);
struct SomeRef(
#[entities] Entity,
// We add an ignored field here to ensure `reflect_clone` fails and `FromWorld` is used
#[reflect(ignore)] PhantomData<()>,
);
#[derive(Resource)]
struct FromWorldCalled(bool);
@ -1355,7 +1391,7 @@ mod tests {
impl FromWorld for SomeRef {
fn from_world(world: &mut World) -> Self {
world.insert_resource(FromWorldCalled(true));
SomeRef(Entity::PLACEHOLDER)
SomeRef(Entity::PLACEHOLDER, Default::default())
}
}
let mut world = World::new();
@ -1365,14 +1401,17 @@ mod tests {
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
let c = world.spawn(SomeRef(a)).id();
let c = world.spawn(SomeRef(a, Default::default())).id();
let d = world.spawn_empty().id();
let mut map = EntityHashMap::<Entity>::new();
map.insert(a, b);
map.insert(c, d);
let cloned = EntityCloner::default().clone_entity_mapped(&mut world, c, &mut map);
assert_eq!(*world.entity(cloned).get::<SomeRef>().unwrap(), SomeRef(b));
assert_eq!(
*world.entity(cloned).get::<SomeRef>().unwrap(),
SomeRef(b, Default::default())
);
assert!(world.resource::<FromWorldCalled>().0);
}
}

View File

@ -1,11 +1,11 @@
use core::hash::{BuildHasher, Hasher};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
/// A [`BuildHasher`] that results in a [`EntityHasher`].
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default, Clone))]
pub struct EntityHash;
impl BuildHasher for EntityHash {

View File

@ -3,19 +3,26 @@
//! This module is a lightweight wrapper around `indexmap`'s [`IndexMap`] that is more performant for [`Entity`] keys.
use core::{
cmp::Ordering,
fmt::{self, Debug, Formatter},
hash::BuildHasher,
hash::{BuildHasher, Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
ops::{Deref, DerefMut, Index, IndexMut, RangeBounds},
ops::{
Bound, Deref, DerefMut, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull,
RangeInclusive, RangeTo, RangeToInclusive,
},
ptr,
};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use indexmap::map::{self, IndexMap};
use indexmap::map::{self, IndexMap, IntoValues, ValuesMut};
use super::{Entity, EntityHash, EntitySetIterator, TrustedEntityBorrow};
use bevy_platform_support::prelude::Box;
/// A [`IndexMap`] pre-configured to use [`EntityHash`] hashing.
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
@ -46,6 +53,48 @@ impl<V> EntityIndexMap<V> {
self.0
}
/// Returns a slice of all the key-value pairs in the map.
///
/// Equivalent to [`IndexMap::as_slice`].
pub fn as_slice(&self) -> &Slice<V> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
/// Returns a mutable slice of all the key-value pairs in the map.
///
/// Equivalent to [`IndexMap::as_mut_slice`].
pub fn as_mut_slice(&mut self) -> &mut Slice<V> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { Slice::from_slice_unchecked_mut(self.0.as_mut_slice()) }
}
/// Converts into a boxed slice of all the key-value pairs in the map.
///
/// Equivalent to [`IndexMap::into_boxed_slice`].
pub fn into_boxed_slice(self) -> Box<Slice<V>> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { Slice::from_boxed_slice_unchecked(self.0.into_boxed_slice()) }
}
/// Returns a slice of key-value pairs in the given range of indices.
///
/// Equivalent to [`IndexMap::get_range`].
pub fn get_range<R: RangeBounds<usize>>(&self, range: R) -> Option<&Slice<V>> {
self.0.get_range(range).map(|slice|
// SAFETY: EntityIndexSetSlice is a transparent wrapper around indexmap::set::Slice.
unsafe { Slice::from_slice_unchecked(slice) })
}
/// Returns a mutable slice of key-value pairs in the given range of indices.
///
/// Equivalent to [`IndexMap::get_range_mut`].
pub fn get_range_mut<R: RangeBounds<usize>>(&mut self, range: R) -> Option<&mut Slice<V>> {
self.0.get_range_mut(range).map(|slice|
// SAFETY: EntityIndexSetSlice is a transparent wrapper around indexmap::set::Slice.
unsafe { Slice::from_slice_unchecked_mut(slice) })
}
/// Return an iterator over the key-value pairs of the map, in their order.
///
/// Equivalent to [`IndexMap::iter`].
@ -134,6 +183,62 @@ impl<V, Q: TrustedEntityBorrow + ?Sized> Index<&Q> for EntityIndexMap<V> {
}
}
impl<V> Index<(Bound<usize>, Bound<usize>)> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: (Bound<usize>, Bound<usize>)) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<Range<usize>> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: Range<usize>) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<RangeFrom<usize>> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: RangeFrom<usize>) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<RangeFull> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: RangeFull) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<RangeInclusive<usize>> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: RangeInclusive<usize>) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<RangeTo<usize>> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: RangeTo<usize>) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<RangeToInclusive<usize>> for EntityIndexMap<V> {
type Output = Slice<V>;
fn index(&self, key: RangeToInclusive<usize>) -> &Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl<V> Index<usize> for EntityIndexMap<V> {
type Output = V;
fn index(&self, key: usize) -> &V {
@ -147,6 +252,55 @@ impl<V, Q: TrustedEntityBorrow + ?Sized> IndexMut<&Q> for EntityIndexMap<V> {
}
}
impl<V> IndexMut<(Bound<usize>, Bound<usize>)> for EntityIndexMap<V> {
fn index_mut(&mut self, key: (Bound<usize>, Bound<usize>)) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<Range<usize>> for EntityIndexMap<V> {
fn index_mut(&mut self, key: Range<usize>) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<RangeFrom<usize>> for EntityIndexMap<V> {
fn index_mut(&mut self, key: RangeFrom<usize>) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<RangeFull> for EntityIndexMap<V> {
fn index_mut(&mut self, key: RangeFull) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<RangeInclusive<usize>> for EntityIndexMap<V> {
fn index_mut(&mut self, key: RangeInclusive<usize>) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<RangeTo<usize>> for EntityIndexMap<V> {
fn index_mut(&mut self, key: RangeTo<usize>) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<RangeToInclusive<usize>> for EntityIndexMap<V> {
fn index_mut(&mut self, key: RangeToInclusive<usize>) -> &mut Self::Output {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.index_mut(key)) }
}
}
impl<V> IndexMut<usize> for EntityIndexMap<V> {
fn index_mut(&mut self, key: usize) -> &mut V {
self.0.index_mut(key)
@ -201,6 +355,476 @@ where
impl<V: Eq> Eq for EntityIndexMap<V> {}
/// A dynamically-sized slice of key-value pairs in an [`EntityIndexMap`].
///
/// Equivalent to an [`indexmap::map::Slice<V>`] whose source [`IndexMap`]
/// uses [`EntityHash`].
#[repr(transparent)]
pub struct Slice<V, S = EntityHash>(PhantomData<S>, map::Slice<Entity, V>);
impl<V> Slice<V> {
/// Returns an empty slice.
///
/// Equivalent to [`map::Slice::new`].
pub const fn new<'a>() -> &'a Self {
// SAFETY: The source slice is empty.
unsafe { Self::from_slice_unchecked(map::Slice::new()) }
}
/// Returns an empty mutable slice.
///
/// Equivalent to [`map::Slice::new_mut`].
pub fn new_mut<'a>() -> &'a mut Self {
// SAFETY: The source slice is empty.
unsafe { Self::from_slice_unchecked_mut(map::Slice::new_mut()) }
}
/// Constructs a [`entity::index_map::Slice`] from a [`indexmap::map::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexMap`] using [`EntityHash`].
///
/// [`entity::index_map::Slice`]: `crate::entity::index_map::Slice`
pub const unsafe fn from_slice_unchecked(slice: &map::Slice<Entity, V>) -> &Self {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { &*(ptr::from_ref(slice) as *const Self) }
}
/// Constructs a [`entity::index_map::Slice`] from a [`indexmap::map::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexMap`] using [`EntityHash`].
///
/// [`entity::index_map::Slice`]: `crate::entity::index_map::Slice`
pub const unsafe fn from_slice_unchecked_mut(slice: &mut map::Slice<Entity, V>) -> &mut Self {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { &mut *(ptr::from_mut(slice) as *mut Self) }
}
/// Casts `self` to the inner slice.
pub const fn as_inner(&self) -> &map::Slice<Entity, V> {
&self.1
}
/// Constructs a boxed [`entity::index_map::Slice`] from a boxed [`indexmap::map::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexMap`] using [`EntityHash`].
///
/// [`entity::index_map::Slice`]: `crate::entity::index_map::Slice`
pub unsafe fn from_boxed_slice_unchecked(slice: Box<map::Slice<Entity, V>>) -> Box<Self> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { Box::from_raw(Box::into_raw(slice) as *mut Self) }
}
/// Casts a reference to `self` to the inner slice.
#[expect(
clippy::borrowed_box,
reason = "We wish to access the Box API of the inner type, without consuming it."
)]
pub fn as_boxed_inner(self: &Box<Self>) -> &Box<map::Slice<Entity, V>> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { &*(ptr::from_ref(self).cast::<Box<map::Slice<Entity, V>>>()) }
}
/// Casts `self` to the inner slice.
pub fn into_boxed_inner(self: Box<Self>) -> Box<map::Slice<Entity, V>> {
// SAFETY: Slice is a transparent wrapper around indexmap::map::Slice.
unsafe { Box::from_raw(Box::into_raw(self) as *mut map::Slice<Entity, V>) }
}
/// Get a key-value pair by index, with mutable access to the value.
///
/// Equivalent to [`map::Slice::get_index_mut`].
pub fn get_index_mut(&mut self, index: usize) -> Option<(&Entity, &mut V)> {
self.1.get_index_mut(index)
}
/// Returns a slice of key-value pairs in the given range of indices.
///
/// Equivalent to [`map::Slice::get_range`].
pub fn get_range<R: RangeBounds<usize>>(&self, range: R) -> Option<&Self> {
self.1.get_range(range).map(|slice|
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(slice) })
}
/// Returns a mutable slice of key-value pairs in the given range of indices.
///
/// Equivalent to [`map::Slice::get_range_mut`].
pub fn get_range_mut<R: RangeBounds<usize>>(&mut self, range: R) -> Option<&mut Self> {
self.1.get_range_mut(range).map(|slice|
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(slice) })
}
/// Get the first key-value pair, with mutable access to the value.
///
/// Equivalent to [`map::Slice::first_mut`].
pub fn first_mut(&mut self) -> Option<(&Entity, &mut V)> {
self.1.first_mut()
}
/// Get the last key-value pair, with mutable access to the value.
///
/// Equivalent to [`map::Slice::last_mut`].
pub fn last_mut(&mut self) -> Option<(&Entity, &mut V)> {
self.1.last_mut()
}
/// Divides one slice into two at an index.
///
/// Equivalent to [`map::Slice::split_at`].
pub fn split_at(&self, index: usize) -> (&Self, &Self) {
let (slice_1, slice_2) = self.1.split_at(index);
// SAFETY: These are subslices of a valid slice.
unsafe {
(
Self::from_slice_unchecked(slice_1),
Self::from_slice_unchecked(slice_2),
)
}
}
/// Divides one mutable slice into two at an index.
///
/// Equivalent to [`map::Slice::split_at_mut`].
pub fn split_at_mut(&mut self, index: usize) -> (&mut Self, &mut Self) {
let (slice_1, slice_2) = self.1.split_at_mut(index);
// SAFETY: These are subslices of a valid slice.
unsafe {
(
Self::from_slice_unchecked_mut(slice_1),
Self::from_slice_unchecked_mut(slice_2),
)
}
}
/// Returns the first key-value pair and the rest of the slice,
/// or `None` if it is empty.
///
/// Equivalent to [`map::Slice::split_first`].
pub fn split_first(&self) -> Option<((&Entity, &V), &Self)> {
self.1.split_first().map(|(first, rest)| {
(
first,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(rest) },
)
})
}
/// Returns the first key-value pair and the rest of the slice,
/// with mutable access to the value, or `None` if it is empty.
///
/// Equivalent to [`map::Slice::split_first_mut`].
pub fn split_first_mut(&mut self) -> Option<((&Entity, &mut V), &mut Self)> {
self.1.split_first_mut().map(|(first, rest)| {
(
first,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(rest) },
)
})
}
/// Returns the last key-value pair and the rest of the slice,
/// or `None` if it is empty.
///
/// Equivalent to [`map::Slice::split_last`].
pub fn split_last(&self) -> Option<((&Entity, &V), &Self)> {
self.1.split_last().map(|(last, rest)| {
(
last,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(rest) },
)
})
}
/// Returns the last key-value pair and the rest of the slice,
/// with mutable access to the value, or `None` if it is empty.
///
/// Equivalent to [`map::Slice::split_last_mut`].
pub fn split_last_mut(&mut self) -> Option<((&Entity, &mut V), &mut Self)> {
self.1.split_last_mut().map(|(last, rest)| {
(
last,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(rest) },
)
})
}
/// Return an iterator over the key-value pairs of the map slice.
///
/// Equivalent to [`map::Slice::iter`].
pub fn iter(&self) -> Iter<'_, V> {
Iter(self.1.iter(), PhantomData)
}
/// Return an iterator over the key-value pairs of the map slice.
///
/// Equivalent to [`map::Slice::iter_mut`].
pub fn iter_mut(&mut self) -> IterMut<'_, V> {
IterMut(self.1.iter_mut(), PhantomData)
}
/// Return an iterator over the keys of the map slice.
///
/// Equivalent to [`map::Slice::keys`].
pub fn keys(&self) -> Keys<'_, V> {
Keys(self.1.keys(), PhantomData)
}
/// Return an owning iterator over the keys of the map slice.
///
/// Equivalent to [`map::Slice::into_keys`].
pub fn into_keys(self: Box<Self>) -> IntoKeys<V> {
IntoKeys(self.into_boxed_inner().into_keys(), PhantomData)
}
/// Return an iterator over mutable references to the the values of the map slice.
///
/// Equivalent to [`map::Slice::values_mut`].
pub fn values_mut(&mut self) -> ValuesMut<'_, Entity, V> {
self.1.values_mut()
}
/// Return an owning iterator over the values of the map slice.
///
/// Equivalent to [`map::Slice::into_values`].
pub fn into_values(self: Box<Self>) -> IntoValues<Entity, V> {
self.into_boxed_inner().into_values()
}
}
impl<V> Deref for Slice<V> {
type Target = map::Slice<Entity, V>;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<V: Debug> Debug for Slice<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Slice")
.field(&self.0)
.field(&&self.1)
.finish()
}
}
impl<V: Clone> Clone for Box<Slice<V>> {
fn clone(&self) -> Self {
// SAFETY: This a clone of a valid slice.
unsafe { Slice::from_boxed_slice_unchecked(self.as_boxed_inner().clone()) }
}
}
impl<V> Default for &Slice<V> {
fn default() -> Self {
// SAFETY: The source slice is empty.
unsafe { Slice::from_slice_unchecked(<&map::Slice<Entity, V>>::default()) }
}
}
impl<V> Default for &mut Slice<V> {
fn default() -> Self {
// SAFETY: The source slice is empty.
unsafe { Slice::from_slice_unchecked_mut(<&mut map::Slice<Entity, V>>::default()) }
}
}
impl<V> Default for Box<Slice<V>> {
fn default() -> Self {
// SAFETY: The source slice is empty.
unsafe { Slice::from_boxed_slice_unchecked(<Box<map::Slice<Entity, V>>>::default()) }
}
}
impl<V: Copy> From<&Slice<V>> for Box<Slice<V>> {
fn from(value: &Slice<V>) -> Self {
// SAFETY: This slice is a copy of a valid slice.
unsafe { Slice::from_boxed_slice_unchecked(value.1.into()) }
}
}
impl<V: Hash> Hash for Slice<V> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.1.hash(state);
}
}
impl<'a, V> IntoIterator for &'a Slice<V> {
type Item = (&'a Entity, &'a V);
type IntoIter = Iter<'a, V>;
fn into_iter(self) -> Self::IntoIter {
Iter(self.1.iter(), PhantomData)
}
}
impl<'a, V> IntoIterator for &'a mut Slice<V> {
type Item = (&'a Entity, &'a mut V);
type IntoIter = IterMut<'a, V>;
fn into_iter(self) -> Self::IntoIter {
IterMut(self.1.iter_mut(), PhantomData)
}
}
impl<V> IntoIterator for Box<Slice<V>> {
type Item = (Entity, V);
type IntoIter = IntoIter<V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter(self.into_boxed_inner().into_iter(), PhantomData)
}
}
impl<V: PartialOrd> PartialOrd for Slice<V> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.1.partial_cmp(&other.1)
}
}
impl<V: Ord> Ord for Slice<V> {
fn cmp(&self, other: &Self) -> Ordering {
self.1.cmp(other)
}
}
impl<V: PartialEq> PartialEq for Slice<V> {
fn eq(&self, other: &Self) -> bool {
self.1 == other.1
}
}
impl<V: Eq> Eq for Slice<V> {}
impl<V> Index<(Bound<usize>, Bound<usize>)> for Slice<V> {
type Output = Self;
fn index(&self, key: (Bound<usize>, Bound<usize>)) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<Range<usize>> for Slice<V> {
type Output = Self;
fn index(&self, key: Range<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<RangeFrom<usize>> for Slice<V> {
type Output = Self;
fn index(&self, key: RangeFrom<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<RangeFull> for Slice<V> {
type Output = Self;
fn index(&self, key: RangeFull) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<RangeInclusive<usize>> for Slice<V> {
type Output = Self;
fn index(&self, key: RangeInclusive<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<RangeTo<usize>> for Slice<V> {
type Output = Self;
fn index(&self, key: RangeTo<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<RangeToInclusive<usize>> for Slice<V> {
type Output = Self;
fn index(&self, key: RangeToInclusive<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl<V> Index<usize> for Slice<V> {
type Output = V;
fn index(&self, key: usize) -> &V {
self.1.index(key)
}
}
impl<V> IndexMut<(Bound<usize>, Bound<usize>)> for Slice<V> {
fn index_mut(&mut self, key: (Bound<usize>, Bound<usize>)) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<Range<usize>> for Slice<V> {
fn index_mut(&mut self, key: Range<usize>) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<RangeFrom<usize>> for Slice<V> {
fn index_mut(&mut self, key: RangeFrom<usize>) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<RangeFull> for Slice<V> {
fn index_mut(&mut self, key: RangeFull) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<RangeInclusive<usize>> for Slice<V> {
fn index_mut(&mut self, key: RangeInclusive<usize>) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<RangeTo<usize>> for Slice<V> {
fn index_mut(&mut self, key: RangeTo<usize>) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<RangeToInclusive<usize>> for Slice<V> {
fn index_mut(&mut self, key: RangeToInclusive<usize>) -> &mut Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked_mut(self.1.index_mut(key)) }
}
}
impl<V> IndexMut<usize> for Slice<V> {
fn index_mut(&mut self, key: usize) -> &mut V {
self.1.index_mut(key)
}
}
/// An iterator over the entries of an [`EntityIndexMap`].
///
/// This `struct` is created by the [`EntityIndexMap::iter`] method.
@ -212,6 +836,14 @@ impl<'a, V> Iter<'a, V> {
pub fn into_inner(self) -> map::Iter<'a, Entity, V> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::Iter::as_slice`].
pub fn as_slice(&self) -> &Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
}
impl<'a, V> Deref for Iter<'a, V> {
@ -269,6 +901,22 @@ impl<'a, V> IterMut<'a, V> {
pub fn into_inner(self) -> map::IterMut<'a, Entity, V> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::IterMut::as_slice`].
pub fn as_slice(&self) -> &Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
/// Returns a mutable slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::IterMut::into_slice`].
pub fn into_slice(self) -> &'a mut Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.into_slice()) }
}
}
impl<'a, V> Deref for IterMut<'a, V> {
@ -323,6 +971,22 @@ impl<V> IntoIter<V> {
pub fn into_inner(self) -> map::IntoIter<Entity, V> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::IntoIter::as_slice`].
pub fn as_slice(&self) -> &Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
/// Returns a mutable slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::IntoIter::as_mut_slice`].
pub fn as_mut_slice(&mut self) -> &mut Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked_mut(self.0.as_mut_slice()) }
}
}
impl<V> Deref for IntoIter<V> {
@ -383,6 +1047,14 @@ impl<'a, V> Drain<'a, V> {
pub fn into_inner(self) -> map::Drain<'a, Entity, V> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`map::Drain::as_slice`].
pub fn as_slice(&self) -> &Slice<V> {
// SAFETY: The source IndexMap uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
}
impl<'a, V> Deref for Drain<'a, V> {

View File

@ -3,17 +3,25 @@
//! This module is a lightweight wrapper around `indexmap`'ss [`IndexSet`] that is more performant for [`Entity`] keys.
use core::{
cmp::Ordering,
fmt::{self, Debug, Formatter},
hash::BuildHasher,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
ops::{BitAnd, BitOr, BitXor, Deref, DerefMut, Index, RangeBounds, Sub},
ops::{
BitAnd, BitOr, BitXor, Bound, Deref, DerefMut, Index, Range, RangeBounds, RangeFrom,
RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Sub,
},
ptr,
};
use indexmap::set::{self, IndexSet};
use super::{Entity, EntityHash, EntitySetIterator};
use bevy_platform_support::prelude::Box;
/// An [`IndexSet`] pre-configured to use [`EntityHash`] hashing.
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, Clone, Default)]
@ -43,6 +51,14 @@ impl EntityIndexSet {
self.0
}
/// Returns a slice of all the values in the set.
///
/// Equivalent to [`IndexSet::as_slice`].
pub fn as_slice(&self) -> &Slice {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
/// Clears the `IndexSet` in the given index range, returning those values
/// as a drain iterator.
///
@ -51,12 +67,29 @@ impl EntityIndexSet {
Drain(self.0.drain(range), PhantomData)
}
/// Returns a slice of values in the given range of indices.
///
/// Equivalent to [`IndexSet::get_range`].
pub fn get_range<R: RangeBounds<usize>>(&self, range: R) -> Option<&Slice> {
self.0.get_range(range).map(|slice|
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(slice) })
}
/// Return an iterator over the values of the set, in their order.
///
/// Equivalent to [`IndexSet::iter`].
pub fn iter(&self) -> Iter<'_> {
Iter(self.0.iter(), PhantomData)
}
/// Converts into a boxed slice of all the values in the set.
///
/// Equivalent to [`IndexSet::into_boxed_slice`].
pub fn into_boxed_slice(self) -> Box<Slice> {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { Slice::from_boxed_slice_unchecked(self.0.into_boxed_slice()) }
}
}
impl Deref for EntityIndexSet {
@ -166,6 +199,62 @@ impl PartialEq for EntityIndexSet {
impl Eq for EntityIndexSet {}
impl Index<(Bound<usize>, Bound<usize>)> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: (Bound<usize>, Bound<usize>)) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<Range<usize>> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: Range<usize>) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<RangeFrom<usize>> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: RangeFrom<usize>) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<RangeFull> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: RangeFull) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<RangeInclusive<usize>> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: RangeInclusive<usize>) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<RangeTo<usize>> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: RangeTo<usize>) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<RangeToInclusive<usize>> for EntityIndexSet {
type Output = Slice;
fn index(&self, key: RangeToInclusive<usize>) -> &Self::Output {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.index(key)) }
}
}
impl Index<usize> for EntityIndexSet {
type Output = Entity;
fn index(&self, key: usize) -> &Entity {
@ -173,6 +262,290 @@ impl Index<usize> for EntityIndexSet {
}
}
/// A dynamically-sized slice of values in an [`EntityIndexSet`].
///
/// Equivalent to an [`indexmap::set::Slice<V>`] whose source [`IndexSet`]
/// uses [`EntityHash`].
#[repr(transparent)]
pub struct Slice<S = EntityHash>(PhantomData<S>, set::Slice<Entity>);
impl Slice {
/// Returns an empty slice.
///
/// Equivalent to [`set::Slice::new`].
pub const fn new<'a>() -> &'a Self {
// SAFETY: The source slice is empty.
unsafe { Self::from_slice_unchecked(set::Slice::new()) }
}
/// Constructs a [`entity::index_set::Slice`] from a [`indexmap::set::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexSet`] using [`EntityHash`].
///
/// [`entity::index_set::Slice`]: `crate::entity::index_set::Slice`
pub const unsafe fn from_slice_unchecked(slice: &set::Slice<Entity>) -> &Self {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { &*(ptr::from_ref(slice) as *const Self) }
}
/// Constructs a [`entity::index_set::Slice`] from a [`indexmap::set::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexSet`] using [`EntityHash`].
///
/// [`entity::index_set::Slice`]: `crate::entity::index_set::Slice`
pub const unsafe fn from_slice_unchecked_mut(slice: &mut set::Slice<Entity>) -> &mut Self {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { &mut *(ptr::from_mut(slice) as *mut Self) }
}
/// Casts `self` to the inner slice.
pub const fn as_inner(&self) -> &set::Slice<Entity> {
&self.1
}
/// Constructs a boxed [`entity::index_set::Slice`] from a boxed [`indexmap::set::Slice`] unsafely.
///
/// # Safety
///
/// `slice` must stem from an [`IndexSet`] using [`EntityHash`].
///
/// [`entity::index_set::Slice`]: `crate::entity::index_set::Slice`
pub unsafe fn from_boxed_slice_unchecked(slice: Box<set::Slice<Entity>>) -> Box<Self> {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { Box::from_raw(Box::into_raw(slice) as *mut Self) }
}
/// Casts a reference to `self` to the inner slice.
#[expect(
clippy::borrowed_box,
reason = "We wish to access the Box API of the inner type, without consuming it."
)]
pub fn as_boxed_inner(self: &Box<Self>) -> &Box<set::Slice<Entity>> {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { &*(ptr::from_ref(self).cast::<Box<set::Slice<Entity>>>()) }
}
/// Casts `self` to the inner slice.
pub fn into_boxed_inner(self: Box<Self>) -> Box<set::Slice<Entity>> {
// SAFETY: Slice is a transparent wrapper around indexmap::set::Slice.
unsafe { Box::from_raw(Box::into_raw(self) as *mut set::Slice<Entity>) }
}
/// Returns a slice of values in the given range of indices.
///
/// Equivalent to [`set::Slice::get_range`].
pub fn get_range<R: RangeBounds<usize>>(&self, range: R) -> Option<&Self> {
self.1.get_range(range).map(|slice|
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(slice) })
}
/// Divides one slice into two at an index.
///
/// Equivalent to [`set::Slice::split_at`].
pub fn split_at(&self, index: usize) -> (&Self, &Self) {
let (slice_1, slice_2) = self.1.split_at(index);
// SAFETY: These are subslices of a valid slice.
unsafe {
(
Self::from_slice_unchecked(slice_1),
Self::from_slice_unchecked(slice_2),
)
}
}
/// Returns the first value and the rest of the slice,
/// or `None` if it is empty.
///
/// Equivalent to [`set::Slice::split_first`].
pub fn split_first(&self) -> Option<(&Entity, &Self)> {
self.1.split_first().map(|(first, rest)| {
(
first,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(rest) },
)
})
}
/// Returns the last value and the rest of the slice,
/// or `None` if it is empty.
///
/// Equivalent to [`set::Slice::split_last`].
pub fn split_last(&self) -> Option<(&Entity, &Self)> {
self.1.split_last().map(|(last, rest)| {
(
last,
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(rest) },
)
})
}
/// Return an iterator over the values of the set slice.
///
/// Equivalent to [`set::Slice::iter`].
pub fn iter(&self) -> Iter<'_> {
Iter(self.1.iter(), PhantomData)
}
}
impl Deref for Slice {
type Target = set::Slice<Entity>;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'a> IntoIterator for &'a Slice {
type IntoIter = Iter<'a>;
type Item = &'a Entity;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl IntoIterator for Box<Slice> {
type IntoIter = IntoIter;
type Item = Entity;
fn into_iter(self) -> Self::IntoIter {
IntoIter(self.into_boxed_inner().into_iter(), PhantomData)
}
}
impl Clone for Box<Slice> {
fn clone(&self) -> Self {
// SAFETY: This is a clone of a valid slice.
unsafe { Slice::from_boxed_slice_unchecked(self.as_boxed_inner().clone()) }
}
}
impl Default for &Slice {
fn default() -> Self {
// SAFETY: The source slice is empty.
unsafe { Slice::from_slice_unchecked(<&set::Slice<Entity>>::default()) }
}
}
impl Default for Box<Slice> {
fn default() -> Self {
// SAFETY: The source slice is empty.
unsafe { Slice::from_boxed_slice_unchecked(<Box<set::Slice<Entity>>>::default()) }
}
}
impl<V: Debug> Debug for Slice<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Slice")
.field(&self.0)
.field(&&self.1)
.finish()
}
}
impl From<&Slice> for Box<Slice> {
fn from(value: &Slice) -> Self {
// SAFETY: This slice is a copy of a valid slice.
unsafe { Slice::from_boxed_slice_unchecked(value.1.into()) }
}
}
impl Hash for Slice {
fn hash<H: Hasher>(&self, state: &mut H) {
self.1.hash(state);
}
}
impl PartialOrd for Slice {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Slice {
fn cmp(&self, other: &Self) -> Ordering {
self.1.cmp(other)
}
}
impl PartialEq for Slice {
fn eq(&self, other: &Self) -> bool {
self.1 == other.1
}
}
impl Eq for Slice {}
impl Index<(Bound<usize>, Bound<usize>)> for Slice {
type Output = Self;
fn index(&self, key: (Bound<usize>, Bound<usize>)) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<Range<usize>> for Slice {
type Output = Self;
fn index(&self, key: Range<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<RangeFrom<usize>> for Slice {
type Output = Slice;
fn index(&self, key: RangeFrom<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<RangeFull> for Slice {
type Output = Self;
fn index(&self, key: RangeFull) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<RangeInclusive<usize>> for Slice {
type Output = Self;
fn index(&self, key: RangeInclusive<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<RangeTo<usize>> for Slice {
type Output = Self;
fn index(&self, key: RangeTo<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<RangeToInclusive<usize>> for Slice {
type Output = Self;
fn index(&self, key: RangeToInclusive<usize>) -> &Self {
// SAFETY: This a subslice of a valid slice.
unsafe { Self::from_slice_unchecked(self.1.index(key)) }
}
}
impl Index<usize> for Slice {
type Output = Entity;
fn index(&self, key: usize) -> &Entity {
self.1.index(key)
}
}
/// An iterator over the items of an [`EntityIndexSet`].
///
/// This struct is created by the [`iter`] method on [`EntityIndexSet`]. See its documentation for more.
@ -185,6 +558,14 @@ impl<'a> Iter<'a> {
pub fn into_inner(self) -> set::Iter<'a, Entity> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`set::Iter::as_slice`].
pub fn as_slice(&self) -> &Slice {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
}
impl<'a> Deref for Iter<'a> {
@ -246,6 +627,14 @@ impl IntoIter {
pub fn into_inner(self) -> set::IntoIter<Entity> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.
///
/// Equivalent to [`set::IntoIter::as_slice`].
pub fn as_slice(&self) -> &Slice {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
}
impl Deref for IntoIter {
@ -310,6 +699,14 @@ impl<'a> Drain<'a> {
pub fn into_inner(self) -> set::Drain<'a, Entity> {
self.0
}
/// Returns a slice of the remaining entries in the iterator.$
///
/// Equivalent to [`set::Drain::as_slice`].
pub fn as_slice(&self) -> &Slice {
// SAFETY: The source IndexSet uses EntityHash.
unsafe { Slice::from_slice_unchecked(self.0.as_slice()) }
}
}
impl<'a> Deref for Drain<'a> {

View File

@ -166,7 +166,7 @@ type IdCursor = isize;
#[derive(Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
#[cfg_attr(feature = "bevy_reflect", reflect(Hash, PartialEq, Debug))]
#[cfg_attr(feature = "bevy_reflect", reflect(Hash, PartialEq, Debug, Clone))]
#[cfg_attr(
all(feature = "bevy_reflect", feature = "serialize"),
reflect(Serialize, Deserialize)

View File

@ -22,7 +22,7 @@ use bevy_platform_support::sync::Arc;
use super::{
unique_slice::{self, UniqueEntitySlice},
TrustedEntityBorrow, UniqueEntityIter,
Entity, TrustedEntityBorrow, UniqueEntityIter,
};
/// An array that contains only unique entities.
@ -30,9 +30,9 @@ use super::{
/// It can be obtained through certain methods on [`UniqueEntitySlice`],
/// and some [`TryFrom`] implementations.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UniqueEntityArray<T: TrustedEntityBorrow, const N: usize>([T; N]);
pub struct UniqueEntityArray<const N: usize, T: TrustedEntityBorrow = Entity>([T; N]);
impl<T: TrustedEntityBorrow, const N: usize> UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> UniqueEntityArray<N, T> {
/// Constructs a `UniqueEntityArray` from a [`[T; N]`] unsafely.
///
/// # Safety
@ -127,12 +127,12 @@ impl<T: TrustedEntityBorrow, const N: usize> UniqueEntityArray<T, N> {
/// size as `self`.
///
/// Equivalent to [`[T; N]::as_ref`](array::each_ref).
pub fn each_ref(&self) -> UniqueEntityArray<&T, N> {
pub fn each_ref(&self) -> UniqueEntityArray<N, &T> {
UniqueEntityArray(self.0.each_ref())
}
}
impl<T: TrustedEntityBorrow, const N: usize> Deref for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Deref for UniqueEntityArray<N, T> {
type Target = UniqueEntitySlice<T>;
fn deref(&self) -> &Self::Target {
@ -141,19 +141,19 @@ impl<T: TrustedEntityBorrow, const N: usize> Deref for UniqueEntityArray<T, N> {
}
}
impl<T: TrustedEntityBorrow, const N: usize> DerefMut for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> DerefMut for UniqueEntityArray<N, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: All elements in the original array are unique.
unsafe { UniqueEntitySlice::from_slice_unchecked_mut(&mut self.0) }
}
}
impl<T: TrustedEntityBorrow> Default for UniqueEntityArray<T, 0> {
impl<T: TrustedEntityBorrow> Default for UniqueEntityArray<0, T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<'a, T: TrustedEntityBorrow, const N: usize> IntoIterator for &'a UniqueEntityArray<T, N> {
impl<'a, T: TrustedEntityBorrow, const N: usize> IntoIterator for &'a UniqueEntityArray<N, T> {
type Item = &'a T;
type IntoIter = unique_slice::Iter<'a, T>;
@ -164,10 +164,10 @@ impl<'a, T: TrustedEntityBorrow, const N: usize> IntoIterator for &'a UniqueEnti
}
}
impl<T: TrustedEntityBorrow, const N: usize> IntoIterator for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> IntoIterator for UniqueEntityArray<N, T> {
type Item = T;
type IntoIter = IntoIter<T, N>;
type IntoIter = IntoIter<N, T>;
fn into_iter(self) -> Self::IntoIter {
// SAFETY: All elements in the original array are unique.
@ -176,7 +176,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IntoIterator for UniqueEntityArray<
}
impl<T: TrustedEntityBorrow, const N: usize> AsRef<UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn as_ref(&self) -> &UniqueEntitySlice<T> {
self
@ -184,7 +184,7 @@ impl<T: TrustedEntityBorrow, const N: usize> AsRef<UniqueEntitySlice<T>>
}
impl<T: TrustedEntityBorrow, const N: usize> AsMut<UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn as_mut(&mut self) -> &mut UniqueEntitySlice<T> {
self
@ -192,7 +192,7 @@ impl<T: TrustedEntityBorrow, const N: usize> AsMut<UniqueEntitySlice<T>>
}
impl<T: TrustedEntityBorrow, const N: usize> Borrow<UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn borrow(&self) -> &UniqueEntitySlice<T> {
self
@ -200,7 +200,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Borrow<UniqueEntitySlice<T>>
}
impl<T: TrustedEntityBorrow, const N: usize> BorrowMut<UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn borrow_mut(&mut self) -> &mut UniqueEntitySlice<T> {
self
@ -208,7 +208,7 @@ impl<T: TrustedEntityBorrow, const N: usize> BorrowMut<UniqueEntitySlice<T>>
}
impl<T: TrustedEntityBorrow, const N: usize> Index<(Bound<usize>, Bound<usize>)>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Output = UniqueEntitySlice<T>;
fn index(&self, key: (Bound<usize>, Bound<usize>)) -> &Self::Output {
@ -217,7 +217,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<(Bound<usize>, Bound<usize>)>
}
}
impl<T: TrustedEntityBorrow, const N: usize> Index<Range<usize>> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Index<Range<usize>> for UniqueEntityArray<N, T> {
type Output = UniqueEntitySlice<T>;
fn index(&self, key: Range<usize>) -> &Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -225,7 +225,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<Range<usize>> for UniqueEntit
}
}
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFrom<usize>> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFrom<usize>> for UniqueEntityArray<N, T> {
type Output = UniqueEntitySlice<T>;
fn index(&self, key: RangeFrom<usize>) -> &Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -233,7 +233,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFrom<usize>> for UniqueE
}
}
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFull> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFull> for UniqueEntityArray<N, T> {
type Output = UniqueEntitySlice<T>;
fn index(&self, key: RangeFull) -> &Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -242,7 +242,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<RangeFull> for UniqueEntityAr
}
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeInclusive<usize>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Output = UniqueEntitySlice<T>;
fn index(&self, key: RangeInclusive<usize>) -> &Self::Output {
@ -251,7 +251,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<RangeInclusive<usize>>
}
}
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeTo<usize>> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeTo<usize>> for UniqueEntityArray<N, T> {
type Output = UniqueEntitySlice<T>;
fn index(&self, key: RangeTo<usize>) -> &Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -260,7 +260,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<RangeTo<usize>> for UniqueEnt
}
impl<T: TrustedEntityBorrow, const N: usize> Index<RangeToInclusive<usize>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Output = UniqueEntitySlice<T>;
fn index(&self, key: RangeToInclusive<usize>) -> &Self::Output {
@ -269,7 +269,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<RangeToInclusive<usize>>
}
}
impl<T: TrustedEntityBorrow, const N: usize> Index<usize> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> Index<usize> for UniqueEntityArray<N, T> {
type Output = T;
fn index(&self, key: usize) -> &T {
self.0.index(key)
@ -277,7 +277,7 @@ impl<T: TrustedEntityBorrow, const N: usize> Index<usize> for UniqueEntityArray<
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<(Bound<usize>, Bound<usize>)>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn index_mut(&mut self, key: (Bound<usize>, Bound<usize>)) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -285,7 +285,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<(Bound<usize>, Bound<usize
}
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<Range<usize>> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<Range<usize>> for UniqueEntityArray<N, T> {
fn index_mut(&mut self, key: Range<usize>) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
unsafe { UniqueEntitySlice::from_slice_unchecked_mut(self.0.index_mut(key)) }
@ -293,7 +293,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<Range<usize>> for UniqueEn
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeFrom<usize>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn index_mut(&mut self, key: RangeFrom<usize>) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -301,7 +301,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeFrom<usize>>
}
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeFull> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeFull> for UniqueEntityArray<N, T> {
fn index_mut(&mut self, key: RangeFull) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
unsafe { UniqueEntitySlice::from_slice_unchecked_mut(self.0.index_mut(key)) }
@ -309,7 +309,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeFull> for UniqueEntit
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeInclusive<usize>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn index_mut(&mut self, key: RangeInclusive<usize>) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -317,7 +317,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeInclusive<usize>>
}
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeTo<usize>> for UniqueEntityArray<T, N> {
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeTo<usize>> for UniqueEntityArray<N, T> {
fn index_mut(&mut self, key: RangeTo<usize>) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
unsafe { UniqueEntitySlice::from_slice_unchecked_mut(self.0.index_mut(key)) }
@ -325,7 +325,7 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeTo<usize>> for Unique
}
impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeToInclusive<usize>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
fn index_mut(&mut self, key: RangeToInclusive<usize>) -> &mut Self::Output {
// SAFETY: All elements in the original slice are unique.
@ -333,148 +333,148 @@ impl<T: TrustedEntityBorrow, const N: usize> IndexMut<RangeToInclusive<usize>>
}
}
impl<T: TrustedEntityBorrow + Clone> From<&[T; 1]> for UniqueEntityArray<T, 1> {
impl<T: TrustedEntityBorrow + Clone> From<&[T; 1]> for UniqueEntityArray<1, T> {
fn from(value: &[T; 1]) -> Self {
Self(value.clone())
}
}
impl<T: TrustedEntityBorrow + Clone> From<&[T; 0]> for UniqueEntityArray<T, 0> {
impl<T: TrustedEntityBorrow + Clone> From<&[T; 0]> for UniqueEntityArray<0, T> {
fn from(value: &[T; 0]) -> Self {
Self(value.clone())
}
}
impl<T: TrustedEntityBorrow + Clone> From<&mut [T; 1]> for UniqueEntityArray<T, 1> {
impl<T: TrustedEntityBorrow + Clone> From<&mut [T; 1]> for UniqueEntityArray<1, T> {
fn from(value: &mut [T; 1]) -> Self {
Self(value.clone())
}
}
impl<T: TrustedEntityBorrow + Clone> From<&mut [T; 0]> for UniqueEntityArray<T, 0> {
impl<T: TrustedEntityBorrow + Clone> From<&mut [T; 0]> for UniqueEntityArray<0, T> {
fn from(value: &mut [T; 0]) -> Self {
Self(value.clone())
}
}
impl<T: TrustedEntityBorrow> From<[T; 1]> for UniqueEntityArray<T, 1> {
impl<T: TrustedEntityBorrow> From<[T; 1]> for UniqueEntityArray<1, T> {
fn from(value: [T; 1]) -> Self {
Self(value)
}
}
impl<T: TrustedEntityBorrow> From<[T; 0]> for UniqueEntityArray<T, 0> {
impl<T: TrustedEntityBorrow> From<[T; 0]> for UniqueEntityArray<0, T> {
fn from(value: [T; 0]) -> Self {
Self(value)
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 1>> for (T,) {
fn from(array: UniqueEntityArray<T, 1>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<1, T>> for (T,) {
fn from(array: UniqueEntityArray<1, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 2>> for (T, T) {
fn from(array: UniqueEntityArray<T, 2>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<2, T>> for (T, T) {
fn from(array: UniqueEntityArray<2, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 3>> for (T, T, T) {
fn from(array: UniqueEntityArray<T, 3>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<3, T>> for (T, T, T) {
fn from(array: UniqueEntityArray<3, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 4>> for (T, T, T, T) {
fn from(array: UniqueEntityArray<T, 4>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<4, T>> for (T, T, T, T) {
fn from(array: UniqueEntityArray<4, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 5>> for (T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 5>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<5, T>> for (T, T, T, T, T) {
fn from(array: UniqueEntityArray<5, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 6>> for (T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 6>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<6, T>> for (T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<6, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 7>> for (T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 7>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<7, T>> for (T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<7, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 8>> for (T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 8>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<8, T>> for (T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<8, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 9>> for (T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 9>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<9, T>> for (T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<9, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 10>> for (T, T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 10>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<10, T>> for (T, T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<10, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 11>> for (T, T, T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<T, 11>) -> Self {
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<11, T>> for (T, T, T, T, T, T, T, T, T, T, T) {
fn from(array: UniqueEntityArray<11, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<T, 12>>
impl<T: TrustedEntityBorrow> From<UniqueEntityArray<12, T>>
for (T, T, T, T, T, T, T, T, T, T, T, T)
{
fn from(array: UniqueEntityArray<T, 12>) -> Self {
fn from(array: UniqueEntityArray<12, T>) -> Self {
Self::from(array.into_inner())
}
}
impl<T: TrustedEntityBorrow + Ord, const N: usize> From<UniqueEntityArray<T, N>> for BTreeSet<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow + Ord, const N: usize> From<UniqueEntityArray<N, T>> for BTreeSet<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
BTreeSet::from(value.0)
}
}
impl<T: TrustedEntityBorrow + Ord, const N: usize> From<UniqueEntityArray<T, N>> for BinaryHeap<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow + Ord, const N: usize> From<UniqueEntityArray<N, T>> for BinaryHeap<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
BinaryHeap::from(value.0)
}
}
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<T, N>> for LinkedList<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<N, T>> for LinkedList<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
LinkedList::from(value.0)
}
}
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<T, N>> for Vec<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<N, T>> for Vec<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
Vec::from(value.0)
}
}
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<T, N>> for VecDeque<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<N, T>> for VecDeque<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
VecDeque::from(value.0)
}
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<&UniqueEntitySlice<U>> for UniqueEntityArray<T, N>
PartialEq<&UniqueEntitySlice<U>> for UniqueEntityArray<N, T>
{
fn eq(&self, other: &&UniqueEntitySlice<U>) -> bool {
self.0.eq(&other.as_inner())
@ -482,47 +482,47 @@ impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usi
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<UniqueEntitySlice<U>> for UniqueEntityArray<T, N>
PartialEq<UniqueEntitySlice<U>> for UniqueEntityArray<N, T>
{
fn eq(&self, other: &UniqueEntitySlice<U>) -> bool {
self.0.eq(other.as_inner())
}
}
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<&UniqueEntityArray<U, N>>
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<&UniqueEntityArray<N, U>>
for Vec<T>
{
fn eq(&self, other: &&UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &&UniqueEntityArray<N, U>) -> bool {
self.eq(&other.0)
}
}
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<&UniqueEntityArray<U, N>>
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<&UniqueEntityArray<N, U>>
for VecDeque<T>
{
fn eq(&self, other: &&UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &&UniqueEntityArray<N, U>) -> bool {
self.eq(&other.0)
}
}
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<&mut UniqueEntityArray<U, N>> for VecDeque<T>
PartialEq<&mut UniqueEntityArray<N, U>> for VecDeque<T>
{
fn eq(&self, other: &&mut UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &&mut UniqueEntityArray<N, U>) -> bool {
self.eq(&other.0)
}
}
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<UniqueEntityArray<U, N>>
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<UniqueEntityArray<N, U>>
for Vec<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.eq(&other.0)
}
}
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<UniqueEntityArray<U, N>>
impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<UniqueEntityArray<N, U>>
for VecDeque<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.eq(&other.0)
}
}
@ -530,7 +530,7 @@ impl<T: PartialEq<U>, U: TrustedEntityBorrow, const N: usize> PartialEq<UniqueEn
/// A by-value array iterator.
///
/// Equivalent to [`array::IntoIter`].
pub type IntoIter<T, const N: usize> = UniqueEntityIter<array::IntoIter<T, N>>;
pub type IntoIter<const N: usize, T = Entity> = UniqueEntityIter<array::IntoIter<T, N>>;
impl<T: TrustedEntityBorrow, const N: usize> UniqueEntityIter<array::IntoIter<T, N>> {
/// Returns an immutable slice of all elements that have not been yielded

View File

@ -27,7 +27,8 @@ use bevy_platform_support::sync::Arc;
use super::{
unique_array::UniqueEntityArray,
unique_vec::{self, UniqueEntityVec},
EntitySet, EntitySetIterator, FromEntitySetIterator, TrustedEntityBorrow, UniqueEntityIter,
Entity, EntitySet, EntitySetIterator, FromEntitySetIterator, TrustedEntityBorrow,
UniqueEntityIter,
};
/// A slice that contains only unique entities.
@ -35,7 +36,7 @@ use super::{
/// It can be obtained by slicing [`UniqueEntityVec`].
#[repr(transparent)]
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UniqueEntitySlice<T: TrustedEntityBorrow>([T]);
pub struct UniqueEntitySlice<T: TrustedEntityBorrow = Entity>([T]);
impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Constructs a `UniqueEntitySlice` from a [`&[T]`] unsafely.
@ -136,7 +137,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Returns an array reference to the first `N` items in the slice.
///
/// Equivalent to [`[T]::first_chunk`](slice::first_chunk).
pub const fn first_chunk<const N: usize>(&self) -> Option<&UniqueEntityArray<T, N>> {
pub const fn first_chunk<const N: usize>(&self) -> Option<&UniqueEntityArray<N, T>> {
let Some(chunk) = self.0.first_chunk() else {
return None;
};
@ -149,7 +150,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split_first_chunk`](slice::split_first_chunk).
pub const fn split_first_chunk<const N: usize>(
&self,
) -> Option<(&UniqueEntityArray<T, N>, &UniqueEntitySlice<T>)> {
) -> Option<(&UniqueEntityArray<N, T>, &UniqueEntitySlice<T>)> {
let Some((chunk, rest)) = self.0.split_first_chunk() else {
return None;
};
@ -167,7 +168,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split_last_chunk`](slice::split_last_chunk).
pub const fn split_last_chunk<const N: usize>(
&self,
) -> Option<(&UniqueEntitySlice<T>, &UniqueEntityArray<T, N>)> {
) -> Option<(&UniqueEntitySlice<T>, &UniqueEntityArray<N, T>)> {
let Some((rest, chunk)) = self.0.split_last_chunk() else {
return None;
};
@ -183,7 +184,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Returns an array reference to the last `N` items in the slice.
///
/// Equivalent to [`[T]::last_chunk`](slice::last_chunk).
pub const fn last_chunk<const N: usize>(&self) -> Option<&UniqueEntityArray<T, N>> {
pub const fn last_chunk<const N: usize>(&self) -> Option<&UniqueEntityArray<N, T>> {
let Some(chunk) = self.0.last_chunk() else {
return None;
};
@ -414,7 +415,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::chunk_by`].
///
/// [`[T]::chunk_by`]: `slice::chunk_by`
pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, F, T>
where
F: FnMut(&T, &T) -> bool,
{
@ -428,7 +429,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::chunk_by_mut`].
///
/// [`[T]::chunk_by_mut`]: `slice::chunk_by_mut`
pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, F, T>
where
F: FnMut(&T, &T) -> bool,
{
@ -548,7 +549,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split`].
///
/// [`[T]::split`]: `slice::split`
pub fn split<F>(&self, pred: F) -> Split<'_, T, F>
pub fn split<F>(&self, pred: F) -> Split<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -562,7 +563,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split_mut`].
///
/// [`[T]::split_mut`]: `slice::split_mut`
pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, T, F>
pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -578,7 +579,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split_inclusive`].
///
/// [`[T]::split_inclusive`]: `slice::split_inclusive`
pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -594,7 +595,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::split_inclusive_mut`].
///
/// [`[T]::split_inclusive_mut`]: `slice::split_inclusive_mut`
pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -612,7 +613,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::rsplit`].
///
/// [`[T]::rsplit`]: `slice::rsplit`
pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, T, F>
pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -627,7 +628,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::rsplit_mut`].
///
/// [`[T]::rsplit_mut`]: `slice::rsplit_mut`
pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, T, F>
pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -643,7 +644,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::splitn`].
///
/// [`[T]::splitn`]: `slice::splitn`
pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, T, F>
pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -657,7 +658,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::splitn_mut`].
///
/// [`[T]::splitn_mut`]: `slice::splitn_mut`
pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F>
pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -673,7 +674,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::rsplitn`].
///
/// [`[T]::rsplitn`]: `slice::rsplitn`
pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, T, F>
pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -687,7 +688,7 @@ impl<T: TrustedEntityBorrow> UniqueEntitySlice<T> {
/// Equivalent to [`[T]::rsplitn_mut`].
///
/// [`[T]::rsplitn_mut`]: `slice::rsplitn_mut`
pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F>
pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, F, T>
where
F: FnMut(&T) -> bool,
{
@ -1012,10 +1013,10 @@ impl<'a, T: TrustedEntityBorrow + Clone> From<&'a UniqueEntitySlice<T>>
}
}
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<UniqueEntityArray<T, N>>
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<UniqueEntityArray<N, T>>
for Box<UniqueEntitySlice<T>>
{
fn from(value: UniqueEntityArray<T, N>) -> Self {
fn from(value: UniqueEntityArray<N, T>) -> Self {
// SAFETY: All elements in the original slice are unique.
unsafe { UniqueEntitySlice::from_boxed_slice_unchecked(Box::new(value.into_inner())) }
}
@ -1207,25 +1208,25 @@ impl<T: TrustedEntityBorrow + PartialEq<U>, U, const N: usize> PartialEq<[U; N]>
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<UniqueEntityArray<U, N>> for &UniqueEntitySlice<T>
PartialEq<UniqueEntityArray<N, U>> for &UniqueEntitySlice<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.0.eq(&other.0)
}
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<UniqueEntityArray<U, N>> for &mut UniqueEntitySlice<T>
PartialEq<UniqueEntityArray<N, U>> for &mut UniqueEntitySlice<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.0.eq(&other.0)
}
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<UniqueEntityArray<U, N>> for UniqueEntitySlice<T>
PartialEq<UniqueEntityArray<N, U>> for UniqueEntitySlice<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.0.eq(&other.0)
}
}
@ -1258,7 +1259,7 @@ impl<T: TrustedEntityBorrow + Clone> ToOwned for UniqueEntitySlice<T> {
}
impl<'a, T: TrustedEntityBorrow + Copy, const N: usize> TryFrom<&'a UniqueEntitySlice<T>>
for &'a UniqueEntityArray<T, N>
for &'a UniqueEntityArray<N, T>
{
type Error = TryFromSliceError;
@ -1270,7 +1271,7 @@ impl<'a, T: TrustedEntityBorrow + Copy, const N: usize> TryFrom<&'a UniqueEntity
}
impl<T: TrustedEntityBorrow + Copy, const N: usize> TryFrom<&UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Error = TryFromSliceError;
@ -1280,7 +1281,7 @@ impl<T: TrustedEntityBorrow + Copy, const N: usize> TryFrom<&UniqueEntitySlice<T
}
impl<T: TrustedEntityBorrow + Copy, const N: usize> TryFrom<&mut UniqueEntitySlice<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Error = TryFromSliceError;
@ -1527,19 +1528,19 @@ impl<'a, T: TrustedEntityBorrow + 'a, I: Iterator<Item = &'a [T]> + AsRef<[&'a [
/// An iterator over overlapping subslices of length `size`.
///
/// This struct is created by [`UniqueEntitySlice::windows`].
pub type Windows<'a, T> = UniqueEntitySliceIter<'a, T, slice::Windows<'a, T>>;
pub type Windows<'a, T = Entity> = UniqueEntitySliceIter<'a, T, slice::Windows<'a, T>>;
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the beginning of the slice.
///
/// This struct is created by [`UniqueEntitySlice::chunks`].
pub type Chunks<'a, T> = UniqueEntitySliceIter<'a, T, slice::Chunks<'a, T>>;
pub type Chunks<'a, T = Entity> = UniqueEntitySliceIter<'a, T, slice::Chunks<'a, T>>;
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the beginning of the slice.
///
/// This struct is created by [`UniqueEntitySlice::chunks_exact`].
pub type ChunksExact<'a, T> = UniqueEntitySliceIter<'a, T, slice::ChunksExact<'a, T>>;
pub type ChunksExact<'a, T = Entity> = UniqueEntitySliceIter<'a, T, slice::ChunksExact<'a, T>>;
impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIter<'a, T, slice::ChunksExact<'a, T>> {
/// Returns the remainder of the original slice that is not going to be
@ -1556,13 +1557,13 @@ impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIter<'a, T, slice::ChunksExact
/// time), starting at the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rchunks`].
pub type RChunks<'a, T> = UniqueEntitySliceIter<'a, T, slice::RChunks<'a, T>>;
pub type RChunks<'a, T = Entity> = UniqueEntitySliceIter<'a, T, slice::RChunks<'a, T>>;
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rchunks_exact`].
pub type RChunksExact<'a, T> = UniqueEntitySliceIter<'a, T, slice::RChunksExact<'a, T>>;
pub type RChunksExact<'a, T = Entity> = UniqueEntitySliceIter<'a, T, slice::RChunksExact<'a, T>>;
impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIter<'a, T, slice::RChunksExact<'a, T>> {
/// Returns the remainder of the original slice that is not going to be
@ -1578,38 +1579,39 @@ impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIter<'a, T, slice::RChunksExac
/// An iterator over slice in (non-overlapping) chunks separated by a predicate.
///
/// This struct is created by [`UniqueEntitySlice::chunk_by`].
pub type ChunkBy<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::ChunkBy<'a, T, P>>;
pub type ChunkBy<'a, P, T = Entity> = UniqueEntitySliceIter<'a, T, slice::ChunkBy<'a, T, P>>;
/// An iterator over subslices separated by elements that match a predicate
/// function.
///
/// This struct is created by [`UniqueEntitySlice::split`].
pub type Split<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::Split<'a, T, P>>;
pub type Split<'a, P, T = Entity> = UniqueEntitySliceIter<'a, T, slice::Split<'a, T, P>>;
/// An iterator over subslices separated by elements that match a predicate
/// function.
///
/// This struct is created by [`UniqueEntitySlice::split_inclusive`].
pub type SplitInclusive<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::SplitInclusive<'a, T, P>>;
pub type SplitInclusive<'a, P, T = Entity> =
UniqueEntitySliceIter<'a, T, slice::SplitInclusive<'a, T, P>>;
/// An iterator over subslices separated by elements that match a predicate
/// function, starting from the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rsplit`].
pub type RSplit<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::RSplit<'a, T, P>>;
pub type RSplit<'a, P, T = Entity> = UniqueEntitySliceIter<'a, T, slice::RSplit<'a, T, P>>;
/// An iterator over subslices separated by elements that match a predicate
/// function, limited to a given number of splits.
///
/// This struct is created by [`UniqueEntitySlice::splitn`].
pub type SplitN<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::SplitN<'a, T, P>>;
pub type SplitN<'a, P, T = Entity> = UniqueEntitySliceIter<'a, T, slice::SplitN<'a, T, P>>;
/// An iterator over subslices separated by elements that match a
/// predicate function, limited to a given number of splits, starting
/// from the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rsplitn`].
pub type RSplitN<'a, T, P> = UniqueEntitySliceIter<'a, T, slice::RSplitN<'a, T, P>>;
pub type RSplitN<'a, P, T = Entity> = UniqueEntitySliceIter<'a, T, slice::RSplitN<'a, T, P>>;
/// An iterator that yields `&mut UniqueEntitySlice`. Note that an entity may appear
/// in multiple slices, depending on the wrapped iterator.
@ -1713,13 +1715,14 @@ impl<'a, T: TrustedEntityBorrow + 'a, I: Iterator<Item = &'a mut [T]> + AsMut<[&
/// elements at a time), starting at the beginning of the slice.
///
/// This struct is created by [`UniqueEntitySlice::chunks_mut`].
pub type ChunksMut<'a, T> = UniqueEntitySliceIterMut<'a, T, slice::ChunksMut<'a, T>>;
pub type ChunksMut<'a, T = Entity> = UniqueEntitySliceIterMut<'a, T, slice::ChunksMut<'a, T>>;
/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size`
/// elements at a time), starting at the beginning of the slice.
///
/// This struct is created by [`UniqueEntitySlice::chunks_exact_mut`].
pub type ChunksExactMut<'a, T> = UniqueEntitySliceIterMut<'a, T, slice::ChunksExactMut<'a, T>>;
pub type ChunksExactMut<'a, T = Entity> =
UniqueEntitySliceIterMut<'a, T, slice::ChunksExactMut<'a, T>>;
impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIterMut<'a, T, slice::ChunksExactMut<'a, T>> {
/// Returns the remainder of the original slice that is not going to be
@ -1736,13 +1739,14 @@ impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIterMut<'a, T, slice::ChunksEx
/// elements at a time), starting at the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rchunks_mut`].
pub type RChunksMut<'a, T> = UniqueEntitySliceIterMut<'a, T, slice::RChunksMut<'a, T>>;
pub type RChunksMut<'a, T = Entity> = UniqueEntitySliceIterMut<'a, T, slice::RChunksMut<'a, T>>;
/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size`
/// elements at a time), starting at the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rchunks_exact_mut`].
pub type RChunksExactMut<'a, T> = UniqueEntitySliceIterMut<'a, T, slice::RChunksExactMut<'a, T>>;
pub type RChunksExactMut<'a, T = Entity> =
UniqueEntitySliceIterMut<'a, T, slice::RChunksExactMut<'a, T>>;
impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIterMut<'a, T, slice::RChunksExactMut<'a, T>> {
/// Returns the remainder of the original slice that is not going to be
@ -1759,37 +1763,39 @@ impl<'a, T: TrustedEntityBorrow> UniqueEntitySliceIterMut<'a, T, slice::RChunksE
/// by a predicate.
///
/// This struct is created by [`UniqueEntitySlice::chunk_by_mut`].
pub type ChunkByMut<'a, T, P> = UniqueEntitySliceIterMut<'a, T, slice::ChunkByMut<'a, T, P>>;
pub type ChunkByMut<'a, P, T = Entity> =
UniqueEntitySliceIterMut<'a, T, slice::ChunkByMut<'a, T, P>>;
/// An iterator over the mutable subslices of the vector which are separated
/// by elements that match `pred`.
///
/// This struct is created by [`UniqueEntitySlice::split_mut`].
pub type SplitMut<'a, T, P> = UniqueEntitySliceIterMut<'a, T, slice::SplitMut<'a, T, P>>;
pub type SplitMut<'a, P, T = Entity> = UniqueEntitySliceIterMut<'a, T, slice::SplitMut<'a, T, P>>;
/// An iterator over the mutable subslices of the vector which are separated
/// by elements that match `pred`. Unlike `SplitMut`, it contains the matched
/// parts in the ends of the subslices.
///
/// This struct is created by [`UniqueEntitySlice::split_inclusive_mut`].
pub type SplitInclusiveMut<'a, T, P> =
pub type SplitInclusiveMut<'a, P, T = Entity> =
UniqueEntitySliceIterMut<'a, T, slice::SplitInclusiveMut<'a, T, P>>;
/// An iterator over the subslices of the vector which are separated
/// by elements that match `pred`, starting from the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rsplit_mut`].
pub type RSplitMut<'a, T, P> = UniqueEntitySliceIterMut<'a, T, slice::RSplitMut<'a, T, P>>;
pub type RSplitMut<'a, P, T = Entity> = UniqueEntitySliceIterMut<'a, T, slice::RSplitMut<'a, T, P>>;
/// An iterator over subslices separated by elements that match a predicate
/// function, limited to a given number of splits.
///
/// This struct is created by [`UniqueEntitySlice::splitn_mut`].
pub type SplitNMut<'a, T, P> = UniqueEntitySliceIterMut<'a, T, slice::SplitNMut<'a, T, P>>;
pub type SplitNMut<'a, P, T = Entity> = UniqueEntitySliceIterMut<'a, T, slice::SplitNMut<'a, T, P>>;
/// An iterator over subslices separated by elements that match a
/// predicate function, limited to a given number of splits, starting
/// from the end of the slice.
///
/// This struct is created by [`UniqueEntitySlice::rsplitn_mut`].
pub type RSplitNMut<'a, T, P> = UniqueEntitySliceIterMut<'a, T, slice::RSplitNMut<'a, T, P>>;
pub type RSplitNMut<'a, P, T = Entity> =
UniqueEntitySliceIterMut<'a, T, slice::RSplitNMut<'a, T, P>>;

View File

@ -22,7 +22,7 @@ use bevy_platform_support::sync::Arc;
use super::{
unique_array::UniqueEntityArray,
unique_slice::{self, UniqueEntitySlice},
EntitySet, FromEntitySetIterator, TrustedEntityBorrow, UniqueEntityIter,
Entity, EntitySet, FromEntitySetIterator, TrustedEntityBorrow, UniqueEntityIter,
};
/// A `Vec` that contains only unique entities.
@ -36,7 +36,7 @@ use super::{
/// While this type can be constructed via `Iterator::collect`, doing so is inefficient,
/// and not recommended.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UniqueEntityVec<T: TrustedEntityBorrow>(Vec<T>);
pub struct UniqueEntityVec<T: TrustedEntityBorrow = Entity>(Vec<T>);
impl<T: TrustedEntityBorrow> UniqueEntityVec<T> {
/// Constructs a new, empty `UniqueEntityVec<T>`.
@ -555,9 +555,9 @@ impl<T: TrustedEntityBorrow + PartialEq<U>, U, const N: usize> PartialEq<&[U; N]
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<&UniqueEntityArray<U, N>> for UniqueEntityVec<T>
PartialEq<&UniqueEntityArray<N, U>> for UniqueEntityVec<T>
{
fn eq(&self, other: &&UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &&UniqueEntityArray<N, U>) -> bool {
self.0.eq(&other.as_inner())
}
}
@ -571,9 +571,9 @@ impl<T: TrustedEntityBorrow + PartialEq<U>, U, const N: usize> PartialEq<&mut [U
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<&mut UniqueEntityArray<U, N>> for UniqueEntityVec<T>
PartialEq<&mut UniqueEntityArray<N, U>> for UniqueEntityVec<T>
{
fn eq(&self, other: &&mut UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &&mut UniqueEntityArray<N, U>) -> bool {
self.0.eq(other.as_inner())
}
}
@ -601,9 +601,9 @@ impl<T: TrustedEntityBorrow + PartialEq<U>, U, const N: usize> PartialEq<[U; N]>
}
impl<T: TrustedEntityBorrow + PartialEq<U>, U: TrustedEntityBorrow, const N: usize>
PartialEq<UniqueEntityArray<U, N>> for UniqueEntityVec<T>
PartialEq<UniqueEntityArray<N, U>> for UniqueEntityVec<T>
{
fn eq(&self, other: &UniqueEntityArray<U, N>) -> bool {
fn eq(&self, other: &UniqueEntityArray<N, U>) -> bool {
self.0.eq(other.as_inner())
}
}
@ -711,24 +711,24 @@ impl<T: TrustedEntityBorrow> From<[T; 0]> for UniqueEntityVec<T> {
}
}
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<&UniqueEntityArray<T, N>>
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<&UniqueEntityArray<N, T>>
for UniqueEntityVec<T>
{
fn from(value: &UniqueEntityArray<T, N>) -> Self {
fn from(value: &UniqueEntityArray<N, T>) -> Self {
Self(Vec::from(value.as_inner().clone()))
}
}
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<&mut UniqueEntityArray<T, N>>
impl<T: TrustedEntityBorrow + Clone, const N: usize> From<&mut UniqueEntityArray<N, T>>
for UniqueEntityVec<T>
{
fn from(value: &mut UniqueEntityArray<T, N>) -> Self {
fn from(value: &mut UniqueEntityArray<N, T>) -> Self {
Self(Vec::from(value.as_inner().clone()))
}
}
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<T, N>> for UniqueEntityVec<T> {
fn from(value: UniqueEntityArray<T, N>) -> Self {
impl<T: TrustedEntityBorrow, const N: usize> From<UniqueEntityArray<N, T>> for UniqueEntityVec<T> {
fn from(value: UniqueEntityArray<N, T>) -> Self {
Self(Vec::from(value.into_inner()))
}
}
@ -806,7 +806,7 @@ impl<T: TrustedEntityBorrow, const N: usize> TryFrom<UniqueEntityVec<T>> for Box
}
impl<T: TrustedEntityBorrow, const N: usize> TryFrom<UniqueEntityVec<T>>
for Box<UniqueEntityArray<T, N>>
for Box<UniqueEntityArray<N, T>>
{
type Error = UniqueEntityVec<T>;
@ -828,7 +828,7 @@ impl<T: TrustedEntityBorrow, const N: usize> TryFrom<UniqueEntityVec<T>> for [T;
}
impl<T: TrustedEntityBorrow, const N: usize> TryFrom<UniqueEntityVec<T>>
for UniqueEntityArray<T, N>
for UniqueEntityArray<N, T>
{
type Error = UniqueEntityVec<T>;
@ -1043,7 +1043,7 @@ impl<T: TrustedEntityBorrow> IndexMut<RangeToInclusive<usize>> for UniqueEntityV
///
/// This `struct` is created by the [`IntoIterator::into_iter`] trait
/// method on [`UniqueEntityVec`].
pub type IntoIter<T> = UniqueEntityIter<vec::IntoIter<T>>;
pub type IntoIter<T = Entity> = UniqueEntityIter<vec::IntoIter<T>>;
impl<T: TrustedEntityBorrow> UniqueEntityIter<vec::IntoIter<T>> {
/// Returns the remaining items of this iterator as a slice.
@ -1067,7 +1067,7 @@ impl<T: TrustedEntityBorrow> UniqueEntityIter<vec::IntoIter<T>> {
///
/// This struct is created by [`UniqueEntityVec::drain`].
/// See its documentation for more.
pub type Drain<'a, T> = UniqueEntityIter<vec::Drain<'a, T>>;
pub type Drain<'a, T = Entity> = UniqueEntityIter<vec::Drain<'a, T>>;
impl<'a, T: TrustedEntityBorrow> UniqueEntityIter<vec::Drain<'a, T>> {
/// Returns the remaining items of this iterator as a slice.

View File

@ -76,7 +76,10 @@ use bevy_ecs_macros::{Component, Resource};
use smallvec::SmallVec;
#[cfg(feature = "bevy_reflect")]
use {crate::reflect::ReflectComponent, bevy_reflect::Reflect};
use {
crate::reflect::ReflectComponent, bevy_reflect::std_traits::ReflectDefault,
bevy_reflect::Reflect,
};
/// A marker component for disabled entities.
///
@ -92,12 +95,12 @@ use {crate::reflect::ReflectComponent, bevy_reflect::Reflect};
/// See [the module docs] for more info.
///
/// [the module docs]: crate::entity_disabling
#[derive(Component, Clone, Debug)]
#[derive(Component, Clone, Debug, Default)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Component),
reflect(Debug)
reflect(Debug, Clone, Default)
)]
// This component is registered as a disabling component during World::bootstrap
pub struct Disabled;

View File

@ -0,0 +1,108 @@
use core::{any::type_name, fmt};
use crate::{
entity::Entity,
system::{entity_command::EntityCommandError, Command, EntityCommand},
world::{error::EntityMutableFetchError, World},
};
use super::{default_error_handler, BevyError, ErrorContext};
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
pub trait HandleError<Out = ()> {
/// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
fn handle_error_with(self, error_handler: fn(BevyError, ErrorContext)) -> impl Command;
/// Takes a [`Command`] that returns a Result and uses the default error handler function to convert it into
/// a [`Command`] that internally handles an error if it occurs and returns `()`.
fn handle_error(self) -> impl Command
where
Self: Sized,
{
self.handle_error_with(default_error_handler())
}
}
impl<C, T, E> HandleError<Result<T, E>> for C
where
C: Command<Result<T, E>>,
E: Into<BevyError>,
{
fn handle_error_with(self, error_handler: fn(BevyError, ErrorContext)) -> impl Command {
move |world: &mut World| match self.apply(world) {
Ok(_) => {}
Err(err) => (error_handler)(
err.into(),
ErrorContext::Command {
name: type_name::<C>().into(),
},
),
}
}
}
impl<C> HandleError for C
where
C: Command,
{
#[inline]
fn handle_error_with(self, _error_handler: fn(BevyError, ErrorContext)) -> impl Command {
self
}
#[inline]
fn handle_error(self) -> impl Command
where
Self: Sized,
{
self
}
}
/// Passes in a specific entity to an [`EntityCommand`], resulting in a [`Command`] that
/// internally runs the [`EntityCommand`] on that entity.
///
// NOTE: This is a separate trait from `EntityCommand` because "result-returning entity commands" and
// "non-result returning entity commands" require different implementations, so they cannot be automatically
// implemented. And this isn't the type of implementation that we want to thrust on people implementing
// EntityCommand.
pub trait CommandWithEntity<Out> {
/// Passes in a specific entity to an [`EntityCommand`], resulting in a [`Command`] that
/// internally runs the [`EntityCommand`] on that entity.
fn with_entity(self, entity: Entity) -> impl Command<Out> + HandleError<Out>;
}
impl<C> CommandWithEntity<Result<(), EntityMutableFetchError>> for C
where
C: EntityCommand,
{
fn with_entity(
self,
entity: Entity,
) -> impl Command<Result<(), EntityMutableFetchError>>
+ HandleError<Result<(), EntityMutableFetchError>> {
move |world: &mut World| -> Result<(), EntityMutableFetchError> {
let entity = world.get_entity_mut(entity)?;
self.apply(entity);
Ok(())
}
}
}
impl<C, T, Err> CommandWithEntity<Result<T, EntityCommandError<Err>>> for C
where
C: EntityCommand<Result<T, Err>>,
Err: fmt::Debug + fmt::Display + Send + Sync + 'static,
{
fn with_entity(
self,
entity: Entity,
) -> impl Command<Result<T, EntityCommandError<Err>>> + HandleError<Result<T, EntityCommandError<Err>>>
{
move |world: &mut World| {
let entity = world.get_entity_mut(entity)?;
self.apply(entity)
.map_err(EntityCommandError::CommandFailed)
}
}
}

View File

@ -1,75 +1,171 @@
use crate::{component::Tick, error::BevyError, resource::Resource};
#[cfg(feature = "configurable_error_handler")]
use bevy_platform_support::sync::OnceLock;
use core::fmt::Display;
use crate::{component::Tick, error::BevyError};
use alloc::borrow::Cow;
/// Additional context for a failed system run.
pub struct SystemErrorContext {
/// The name of the system that failed.
pub name: Cow<'static, str>,
/// The last tick that the system was run.
pub last_run: Tick,
/// Context for a [`BevyError`] to aid in debugging.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ErrorContext {
/// The error occurred in a system.
System {
/// The name of the system that failed.
name: Cow<'static, str>,
/// The last tick that the system was run.
last_run: Tick,
},
/// The error occurred in a command.
Command {
/// The name of the command that failed.
name: Cow<'static, str>,
},
/// The error occurred in an observer.
Observer {
/// The name of the observer that failed.
name: Cow<'static, str>,
/// The last tick that the observer was run.
last_run: Tick,
},
}
/// The default systems error handler stored as a resource in the [`World`](crate::world::World).
pub struct DefaultSystemErrorHandler(pub fn(BevyError, SystemErrorContext));
impl Resource for DefaultSystemErrorHandler {}
impl Default for DefaultSystemErrorHandler {
fn default() -> Self {
Self(panic)
impl Display for ErrorContext {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::System { name, .. } => {
write!(f, "System `{}` failed", name)
}
Self::Command { name } => write!(f, "Command `{}` failed", name),
Self::Observer { name, .. } => {
write!(f, "Observer `{}` failed", name)
}
}
}
}
impl ErrorContext {
/// The name of the ECS construct that failed.
pub fn name(&self) -> &str {
match self {
Self::System { name, .. }
| Self::Command { name, .. }
| Self::Observer { name, .. } => name,
}
}
/// A string representation of the kind of ECS construct that failed.
///
/// This is a simpler helper used for logging.
pub fn kind(&self) -> &str {
match self {
Self::System { .. } => "system",
Self::Command { .. } => "command",
Self::Observer { .. } => "observer",
}
}
}
/// A global error handler. This can be set at startup, as long as it is set before
/// any uses. This should generally be configured _before_ initializing the app.
///
/// This should be set inside of your `main` function, before initializing the Bevy app.
/// The value of this error handler can be accessed using the [`default_error_handler`] function,
/// which calls [`OnceLock::get_or_init`] to get the value.
///
/// **Note:** this is only available when the `configurable_error_handler` feature of `bevy_ecs` (or `bevy`) is enabled!
///
/// # Example
///
/// ```
/// # use bevy_ecs::error::{GLOBAL_ERROR_HANDLER, warn};
/// GLOBAL_ERROR_HANDLER.set(warn).expect("The error handler can only be set once, globally.");
/// // initialize Bevy App here
/// ```
///
/// To use this error handler in your app for custom error handling logic:
///
/// ```rust
/// use bevy_ecs::error::{default_error_handler, GLOBAL_ERROR_HANDLER, BevyError, ErrorContext, panic};
///
/// fn handle_errors(error: BevyError, ctx: ErrorContext) {
/// let error_handler = default_error_handler();
/// error_handler(error, ctx);
/// }
/// ```
///
/// # Warning
///
/// As this can *never* be overwritten, library code should never set this value.
#[cfg(feature = "configurable_error_handler")]
pub static GLOBAL_ERROR_HANDLER: OnceLock<fn(BevyError, ErrorContext)> = OnceLock::new();
/// The default error handler. This defaults to [`panic()`],
/// but if set, the [`GLOBAL_ERROR_HANDLER`] will be used instead, enabling error handler customization.
/// The `configurable_error_handler` feature must be enabled to change this from the panicking default behavior,
/// as there may be runtime overhead.
#[inline]
pub fn default_error_handler() -> fn(BevyError, ErrorContext) {
#[cfg(not(feature = "configurable_error_handler"))]
return panic;
#[cfg(feature = "configurable_error_handler")]
return *GLOBAL_ERROR_HANDLER.get_or_init(|| panic);
}
macro_rules! inner {
($call:path, $e:ident, $c:ident) => {
$call!("Encountered an error in system `{}`: {:?}", $c.name, $e);
$call!(
"Encountered an error in {} `{}`: {:?}",
$c.kind(),
$c.name(),
$e
);
};
}
/// Error handler that panics with the system error.
#[track_caller]
#[inline]
pub fn panic(error: BevyError, ctx: SystemErrorContext) {
pub fn panic(error: BevyError, ctx: ErrorContext) {
inner!(panic, error, ctx);
}
/// Error handler that logs the system error at the `error` level.
#[track_caller]
#[inline]
pub fn error(error: BevyError, ctx: SystemErrorContext) {
pub fn error(error: BevyError, ctx: ErrorContext) {
inner!(log::error, error, ctx);
}
/// Error handler that logs the system error at the `warn` level.
#[track_caller]
#[inline]
pub fn warn(error: BevyError, ctx: SystemErrorContext) {
pub fn warn(error: BevyError, ctx: ErrorContext) {
inner!(log::warn, error, ctx);
}
/// Error handler that logs the system error at the `info` level.
#[track_caller]
#[inline]
pub fn info(error: BevyError, ctx: SystemErrorContext) {
pub fn info(error: BevyError, ctx: ErrorContext) {
inner!(log::info, error, ctx);
}
/// Error handler that logs the system error at the `debug` level.
#[track_caller]
#[inline]
pub fn debug(error: BevyError, ctx: SystemErrorContext) {
pub fn debug(error: BevyError, ctx: ErrorContext) {
inner!(log::debug, error, ctx);
}
/// Error handler that logs the system error at the `trace` level.
#[track_caller]
#[inline]
pub fn trace(error: BevyError, ctx: SystemErrorContext) {
pub fn trace(error: BevyError, ctx: ErrorContext) {
inner!(log::trace, error, ctx);
}
/// Error handler that ignores the system error.
#[track_caller]
#[inline]
pub fn ignore(_: BevyError, _: SystemErrorContext) {}
pub fn ignore(_: BevyError, _: ErrorContext) {}

View File

@ -1,18 +1,16 @@
//! Error handling for "fallible" systems.
//! Error handling for Bevy systems, commands, and observers.
//!
//! When a system is added to a [`Schedule`], and its return type is that of [`Result`], then Bevy
//! considers those systems to be "fallible", and the ECS scheduler will special-case the [`Err`]
//! variant of the returned `Result`.
//!
//! All [`BevyError`]s returned by a system are handled by an "error handler". By default, the
//! All [`BevyError`]s returned by a system, observer or command are handled by an "error handler". By default, the
//! [`panic`] error handler function is used, resulting in a panic with the error message attached.
//!
//! You can change the default behavior by registering a custom error handler, either globally or
//! per `Schedule`:
//!
//! - [`App::set_system_error_handler`] sets the global error handler for all systems of the
//! current [`World`].
//! - [`Schedule::set_error_handler`] sets the error handler for all systems of that schedule.
//! You can change the default behavior by registering a custom error handler.
//! Modify the [`GLOBAL_ERROR_HANDLER`] value to set a custom error handler function for your entire app.
//! In practice, this is generally feature-flagged: panicking or loudly logging errors in development,
//! and quietly logging or ignoring them in production to avoid crashing the app.
//!
//! Bevy provides a number of pre-built error-handlers for you to use:
//!
@ -29,52 +27,55 @@
//! signature:
//!
//! ```rust,ignore
//! fn(BevyError, SystemErrorContext)
//! fn(BevyError, ErrorContext)
//! ```
//!
//! The [`SystemErrorContext`] allows you to access additional details relevant to providing
//! context surrounding the system error such as the system's [`name`] in your error messages.
//! The [`ErrorContext`] allows you to access additional details relevant to providing
//! context surrounding the error such as the system's [`name`] in your error messages.
//!
//! For example:
//! Remember to turn on the `configurable_error_handler` feature to set a global error handler!
//!
//! ```rust
//! # use bevy_ecs::prelude::*;
//! # use bevy_ecs::schedule::ScheduleLabel;
//! # use log::trace;
//! # fn update() -> Result { Ok(()) }
//! # #[derive(ScheduleLabel, Hash, Debug, PartialEq, Eq, Clone, Copy)]
//! # struct MySchedule;
//! # fn main() {
//! let mut schedule = Schedule::new(MySchedule);
//! schedule.add_systems(update);
//! schedule.set_error_handler(|error, ctx| {
//! if ctx.name.ends_with("update") {
//! trace!("Nothing to see here, move along.");
//! return;
//! }
//! ```rust, ignore
//! use bevy_ecs::error::{GLOBAL_ERROR_HANDLER, BevyError, ErrorContext};
//! use log::trace;
//!
//! bevy_ecs::error::error(error, ctx);
//! });
//! # }
//! fn my_error_handler(error: BevyError, ctx: ErrorContext) {
//! if ctx.name().ends_with("plz_ignore") {
//! trace!("Nothing to see here, move along.");
//! return;
//! }
//! bevy_ecs::error::error(error, ctx);
//! }
//!
//! fn main() {
//! // This requires the "configurable_error_handler" feature to be enabled to be in scope.
//! GLOBAL_ERROR_HANDLER.set(my_error_handler).expect("The error handler can only be set once.");
//!
//! // Initialize your Bevy App here
//! }
//! ```
//!
//! If you need special handling of individual fallible systems, you can use Bevy's [`system piping
//! feature`] to capture the `Result` output of the system and handle it accordingly.
//! feature`] to capture the [`Result`] output of the system and handle it accordingly.
//!
//! When working with commands, you can handle the result of each command separately using the [`HandleError::handle_error_with`] method.
//!
//! [`Schedule`]: crate::schedule::Schedule
//! [`panic`]: panic()
//! [`World`]: crate::world::World
//! [`Schedule::set_error_handler`]: crate::schedule::Schedule::set_error_handler
//! [`System`]: crate::system::System
//! [`name`]: crate::system::System::name
//! [`App::set_system_error_handler`]: ../../bevy_app/struct.App.html#method.set_system_error_handler
//! [`system piping feature`]: crate::system::In
mod bevy_error;
mod command_handling;
mod handler;
pub use bevy_error::*;
pub use command_handling::*;
pub use handler::*;
/// A result type for use in fallible systems.
/// A result type for use in fallible systems, commands and observers.
///
/// The [`BevyError`] type is a type-erased error type with optional Bevy-specific diagnostics.
pub type Result<T = (), E = BevyError> = core::result::Result<T, E>;

View File

@ -110,14 +110,18 @@ struct EventWrapperComponent<E: Event + ?Sized>(PhantomData<E>);
/// sent to the point it was processed. `EventId`s increase monotonically by send order.
///
/// [`World`]: crate::world::World
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, Debug, PartialEq, Hash)
)]
pub struct EventId<E: Event> {
/// Uniquely identifies the event associated with this ID.
// This value corresponds to the order in which each event was added to the world.
pub id: usize,
/// The source code location that triggered this event.
pub caller: MaybeLocation,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
#[cfg_attr(feature = "bevy_reflect", reflect(ignore, clone))]
pub(super) _marker: PhantomData<E>,
}

View File

@ -320,7 +320,7 @@ impl<E: Event> Extend<E> for Events<E> {
}
#[derive(Debug)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Default))]
pub(crate) struct EventSequence<E: Event> {
pub(crate) events: Vec<EventInstance<E>>,
pub(crate) start_event_count: usize,

View File

@ -12,11 +12,13 @@ use crate::{
bundle::Bundle,
component::{Component, HookContext},
entity::Entity,
relationship::{RelatedSpawner, RelatedSpawnerCommands},
relationship::{RelatedSpawner, RelatedSpawnerCommands, Relationship},
system::EntityCommands,
world::{DeferredWorld, EntityWorldMut, FromWorld, World},
};
use alloc::{format, string::String, vec::Vec};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::std_traits::ReflectDefault;
use core::ops::Deref;
use core::slice;
use disqualified::ShortName;
@ -24,7 +26,7 @@ use log::warn;
/// Stores the parent entity of this child entity with this component.
///
/// This is a [`Relationship`](crate::relationship::Relationship) component, and creates the canonical
/// This is a [`Relationship`] component, and creates the canonical
/// "parent / child" hierarchy. This is the "source of truth" component, and it pairs with
/// the [`Children`] [`RelationshipTarget`](crate::relationship::RelationshipTarget).
///
@ -90,7 +92,7 @@ use log::warn;
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(
feature = "bevy_reflect",
reflect(Component, PartialEq, Debug, FromWorld)
reflect(Component, PartialEq, Debug, FromWorld, Clone)
)]
#[relationship(relationship_target = Children)]
#[doc(alias = "IsChild", alias = "Parent")]
@ -115,7 +117,7 @@ impl FromWorld for ChildOf {
/// Tracks which entities are children of this parent entity.
///
/// A [`RelationshipTarget`] collection component that is populated
/// with entities that "target" this entity with the [`ChildOf`] [`Relationship`](crate::relationship::Relationship) component.
/// with entities that "target" this entity with the [`ChildOf`] [`Relationship`] component.
///
/// Together, these components form the "canonical parent-child hierarchy". See the [`ChildOf`] component for the full
/// description of this relationship and instructions on how to use it.
@ -133,7 +135,7 @@ impl FromWorld for ChildOf {
#[derive(Component, Default, Debug, PartialEq, Eq)]
#[relationship_target(relationship = ChildOf, linked_spawn)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, FromWorld, Default))]
#[doc(alias = "IsParent")]
pub struct Children(Vec<Entity>);
@ -179,6 +181,34 @@ impl<'w> EntityWorldMut<'w> {
self.add_related::<ChildOf>(&[child])
}
/// Replaces all the related children with a new set of children.
pub fn replace_children(&mut self, children: &[Entity]) -> &mut Self {
self.replace_related::<ChildOf>(children)
}
/// Replaces all the related children with a new set of children.
///
/// # Warning
///
/// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
/// Refer to [`Self::replace_related_with_difference`] for a list of these invariants.
///
/// # Panics
///
/// Panics when debug assertions are enabled if an invariant is is broken and the command is executed.
pub fn replace_children_with_difference(
&mut self,
entities_to_unrelate: &[Entity],
entities_to_relate: &[Entity],
newly_related_entities: &[Entity],
) -> &mut Self {
self.replace_related_with_difference::<ChildOf>(
entities_to_unrelate,
entities_to_relate,
newly_related_entities,
)
}
/// Spawns the passed bundle and adds it to this entity as a child.
///
/// For efficient spawning of multiple children, use [`with_children`].
@ -230,6 +260,34 @@ impl<'a> EntityCommands<'a> {
self.add_related::<ChildOf>(&[child])
}
/// Replaces the children on this entity with a new list of children.
pub fn replace_children(&mut self, children: &[Entity]) -> &mut Self {
self.replace_related::<ChildOf>(children)
}
/// Replaces all the related entities with a new set of entities.
///
/// # Warning
///
/// Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
/// Refer to [`EntityWorldMut::replace_related_with_difference`] for a list of these invariants.
///
/// # Panics
///
/// Panics when debug assertions are enabled if an invariant is is broken and the command is executed.
pub fn replace_children_with_difference<R: Relationship>(
&mut self,
entities_to_unrelate: &[Entity],
entities_to_relate: &[Entity],
newly_related_entities: &[Entity],
) -> &mut Self {
self.replace_related_with_difference::<R>(
entities_to_unrelate,
entities_to_relate,
newly_related_entities,
)
}
/// Spawns the passed bundle and adds it to this entity as a child.
///
/// For efficient spawning of multiple children, use [`with_children`].
@ -329,7 +387,7 @@ mod tests {
use crate::{
entity::Entity,
hierarchy::{ChildOf, Children},
relationship::RelationshipTarget,
relationship::{RelationshipHookMode, RelationshipTarget},
spawn::{Spawn, SpawnRelated},
world::World,
};
@ -490,4 +548,313 @@ mod tests {
let id = world.spawn(Children::spawn((Spawn(()), Spawn(())))).id();
assert_eq!(world.entity(id).get::<Children>().unwrap().len(), 2,);
}
#[test]
fn replace_children() {
let mut world = World::new();
let parent = world.spawn(Children::spawn((Spawn(()), Spawn(())))).id();
let &[child_a, child_b] = &world.entity(parent).get::<Children>().unwrap().0[..] else {
panic!("Tried to spawn 2 children on an entity and didn't get 2 children");
};
let child_c = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children(&[child_a, child_c]);
let children = world.entity(parent).get::<Children>().unwrap();
assert!(children.contains(&child_a));
assert!(children.contains(&child_c));
assert!(!children.contains(&child_b));
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert!(world.entity(child_b).get::<ChildOf>().is_none());
}
#[test]
fn replace_children_with_nothing() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
world.entity_mut(parent).add_children(&[child_a, child_b]);
assert_eq!(world.entity(parent).get::<Children>().unwrap().len(), 2);
world.entity_mut(parent).replace_children(&[]);
assert!(world.entity(child_a).get::<ChildOf>().is_none());
assert!(world.entity(child_b).get::<ChildOf>().is_none());
}
#[test]
fn insert_same_child_twice() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child = world.spawn_empty().id();
world.entity_mut(parent).add_child(child);
world.entity_mut(parent).add_child(child);
let children = world.get::<Children>(parent).unwrap();
assert_eq!(children.0, [child]);
assert_eq!(
world.entity(child).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
}
#[test]
fn replace_with_difference() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
// Test inserting new relations
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a, child_b],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_b).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_a, child_b]
);
// Test replacing relations and changing order
world.entity_mut(parent).replace_children_with_difference(
&[child_b],
&[child_d, child_c, child_a],
&[child_c, child_d],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_d).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_d, child_c, child_a]
);
assert!(!world.entity(child_b).contains::<ChildOf>());
// Test removing relationships
world.entity_mut(parent).replace_children_with_difference(
&[child_a, child_d, child_c],
&[],
&[],
);
assert!(!world.entity(parent).contains::<Children>());
assert!(!world.entity(child_a).contains::<ChildOf>());
assert!(!world.entity(child_b).contains::<ChildOf>());
assert!(!world.entity(child_c).contains::<ChildOf>());
assert!(!world.entity(child_d).contains::<ChildOf>());
}
#[test]
fn replace_with_difference_on_empty() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[child_a], &[], &[]);
assert!(!world.entity(parent).contains::<Children>());
assert!(!world.entity(child_a).contains::<ChildOf>());
}
#[test]
fn replace_with_difference_totally_new_children() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
// Test inserting new relations
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a, child_b],
);
assert_eq!(
world.entity(child_a).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_b).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_a, child_b]
);
// Test replacing relations and changing order
world.entity_mut(parent).replace_children_with_difference(
&[child_b, child_a],
&[child_d, child_c],
&[child_c, child_d],
);
assert_eq!(
world.entity(child_c).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(child_d).get::<ChildOf>().unwrap(),
&ChildOf { parent }
);
assert_eq!(
world.entity(parent).get::<Children>().unwrap().0,
[child_d, child_c]
);
assert!(!world.entity(child_a).contains::<ChildOf>());
assert!(!world.entity(child_b).contains::<ChildOf>());
}
#[test]
fn replace_children_order() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
let child_c = world.spawn_empty().id();
let child_d = world.spawn_empty().id();
let initial_order = [child_a, child_b, child_c, child_d];
world.entity_mut(parent).add_children(&initial_order);
assert_eq!(
world.entity_mut(parent).get::<Children>().unwrap().0,
initial_order
);
let new_order = [child_d, child_b, child_a, child_c];
world.entity_mut(parent).replace_children(&new_order);
assert_eq!(world.entity(parent).get::<Children>().unwrap().0, new_order);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_overlapping_unrelate_with_relate() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[], &[child_a], &[child_a]);
// This should panic
world
.entity_mut(parent)
.replace_children_with_difference(&[child_a], &[child_a], &[]);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_overlapping_unrelate_with_newly() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
world
.entity_mut(parent)
.replace_children_with_difference(&[], &[child_a], &[child_a]);
// This should panic
world.entity_mut(parent).replace_children_with_difference(
&[child_b],
&[child_a, child_b],
&[child_b],
);
}
#[test]
#[should_panic]
#[cfg_attr(
not(debug_assertions),
ignore = "we don't check invariants if debug assertions are off"
)]
fn replace_diff_invariant_newly_not_subset() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let child_a = world.spawn_empty().id();
let child_b = world.spawn_empty().id();
// This should panic
world.entity_mut(parent).replace_children_with_difference(
&[],
&[child_a, child_b],
&[child_a],
);
}
#[test]
fn child_replace_hook_skip() {
let mut world = World::new();
let parent = world.spawn_empty().id();
let other = world.spawn_empty().id();
let child = world.spawn(ChildOf { parent }).id();
world.entity_mut(child).insert_with_relationship_hook_mode(
ChildOf { parent: other },
RelationshipHookMode::Skip,
);
assert_eq!(
&**world.entity(parent).get::<Children>().unwrap(),
&[child],
"Children should still have the old value, as on_insert/on_replace didn't run"
);
}
}

View File

@ -21,7 +21,7 @@ pub(crate) mod masks;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug, Hash, PartialEq))]
#[cfg_attr(feature = "bevy_reflect", reflect(Debug, Hash, PartialEq, Clone))]
// Alignment repr necessary to allow LLVM to better output
// optimized codegen for `to_bits`, `PartialEq` and `Ord`.
#[repr(C, align(8))]

View File

@ -41,7 +41,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Component, Default, Debug)
reflect(Component, Default, Debug, Clone, Hash, PartialEq)
)]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),

View File

@ -3,7 +3,7 @@ use core::any::Any;
use crate::{
component::{ComponentHook, ComponentId, HookContext, Mutable, StorageType},
error::{DefaultSystemErrorHandler, SystemErrorContext},
error::{default_error_handler, ErrorContext},
observer::{ObserverDescriptor, ObserverTrigger},
prelude::*,
query::DebugCheckedUnwrap,
@ -273,7 +273,7 @@ pub struct Observer {
system: Box<dyn Any + Send + Sync + 'static>,
descriptor: ObserverDescriptor,
hook_on_add: ComponentHook,
error_handler: Option<fn(BevyError, SystemErrorContext)>,
error_handler: Option<fn(BevyError, ErrorContext)>,
}
impl Observer {
@ -322,7 +322,7 @@ impl Observer {
/// Set the error handler to use for this observer.
///
/// See the [`error` module-level documentation](crate::error) for more information.
pub fn with_error_handler(mut self, error_handler: fn(BevyError, SystemErrorContext)) -> Self {
pub fn with_error_handler(mut self, error_handler: fn(BevyError, ErrorContext)) -> Self {
self.error_handler = Some(error_handler);
self
}
@ -409,7 +409,7 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
if let Err(err) = (*system).run_unsafe(trigger, world) {
error_handler(
err,
SystemErrorContext {
ErrorContext::Observer {
name: (*system).name(),
last_run: (*system).get_last_run(),
},
@ -444,7 +444,7 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
..Default::default()
};
let error_handler = world.get_resource_or_init::<DefaultSystemErrorHandler>().0;
let error_handler = default_error_handler();
// Initialize System
let system: *mut dyn ObserverSystem<E, B> =

View File

@ -1,7 +1,7 @@
use crate::{
archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
component::{ComponentId, Tick},
entity::{Entity, EntityBorrow, EntitySet},
entity::{unique_array::UniqueEntityArray, Entity, EntityBorrow, EntitySet},
entity_disabling::DefaultQueryFilters,
prelude::FromWorld,
query::{Access, FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, WorldQuery},
@ -997,6 +997,44 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
self.query(world).get_many_inner(entities)
}
/// Returns the read-only query results for the given [`UniqueEntityArray`].
///
/// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is
/// returned instead.
///
/// # Examples
///
/// ```
/// use bevy_ecs::{prelude::*, query::QueryEntityError, entity::{EntitySetIterator, unique_array::UniqueEntityArray, unique_vec::UniqueEntityVec}};
///
/// #[derive(Component, PartialEq, Debug)]
/// struct A(usize);
///
/// let mut world = World::new();
/// let entity_set: UniqueEntityVec = world.spawn_batch((0..3).map(A)).collect_set();
/// let entity_set: UniqueEntityArray<3> = entity_set.try_into().unwrap();
///
/// world.spawn(A(73));
///
/// let mut query_state = world.query::<&A>();
///
/// let component_values = query_state.get_many_unique(&world, entity_set).unwrap();
///
/// assert_eq!(component_values, [&A(0), &A(1), &A(2)]);
///
/// let wrong_entity = Entity::from_raw(365);
///
/// assert_eq!(match query_state.get_many_unique(&mut world, UniqueEntityArray::from([wrong_entity])).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity);
/// ```
#[inline]
pub fn get_many_unique<'w, const N: usize>(
&mut self,
world: &'w World,
entities: UniqueEntityArray<N>,
) -> Result<[ROQueryItem<'w, D>; N], QueryEntityError> {
self.query(world).get_many_unique_inner(entities)
}
/// Gets the query result for the given [`World`] and [`Entity`].
///
/// This is always guaranteed to run in `O(1)` time.
@ -1053,7 +1091,52 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
world: &'w mut World,
entities: [Entity; N],
) -> Result<[D::Item<'w>; N], QueryEntityError> {
self.query_mut(world).get_many_inner(entities)
self.query_mut(world).get_many_mut_inner(entities)
}
/// Returns the query results for the given [`UniqueEntityArray`].
///
/// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is
/// returned instead.
///
/// ```
/// use bevy_ecs::{prelude::*, query::QueryEntityError, entity::{EntitySetIterator, unique_array::UniqueEntityArray, unique_vec::UniqueEntityVec}};
///
/// #[derive(Component, PartialEq, Debug)]
/// struct A(usize);
///
/// let mut world = World::new();
///
/// let entity_set: UniqueEntityVec = world.spawn_batch((0..3).map(A)).collect_set();
/// let entity_set: UniqueEntityArray<3> = entity_set.try_into().unwrap();
///
/// world.spawn(A(73));
///
/// let mut query_state = world.query::<&mut A>();
///
/// let mut mutable_component_values = query_state.get_many_unique_mut(&mut world, entity_set).unwrap();
///
/// for mut a in &mut mutable_component_values {
/// a.0 += 5;
/// }
///
/// let component_values = query_state.get_many_unique(&world, entity_set).unwrap();
///
/// assert_eq!(component_values, [&A(5), &A(6), &A(7)]);
///
/// let wrong_entity = Entity::from_raw(57);
/// let invalid_entity = world.spawn_empty().id();
///
/// assert_eq!(match query_state.get_many_unique(&mut world, UniqueEntityArray::from([wrong_entity])).unwrap_err() {QueryEntityError::EntityDoesNotExist(error) => error.entity, _ => panic!()}, wrong_entity);
/// assert_eq!(match query_state.get_many_unique_mut(&mut world, UniqueEntityArray::from([invalid_entity])).unwrap_err() {QueryEntityError::QueryDoesNotMatch(entity, _) => entity, _ => panic!()}, invalid_entity);
/// ```
#[inline]
pub fn get_many_unique_mut<'w, const N: usize>(
&mut self,
world: &'w mut World,
entities: UniqueEntityArray<N>,
) -> Result<[D::Item<'w>; N], QueryEntityError> {
self.query_mut(world).get_many_unique_inner(entities)
}
/// Gets the query result for the given [`World`] and [`Entity`].

View File

@ -11,7 +11,7 @@ use crate::{
bundle::BundleFromComponents,
entity::EntityMapper,
prelude::Bundle,
relationship::RelationshipInsertHookMode,
relationship::RelationshipHookMode,
world::{EntityMut, EntityWorldMut},
};
use bevy_reflect::{
@ -42,7 +42,7 @@ pub struct ReflectBundleFns {
&dyn PartialReflect,
&TypeRegistry,
&mut dyn EntityMapper,
RelationshipInsertHookMode,
RelationshipHookMode,
),
/// Function pointer implementing [`ReflectBundle::remove`].
pub remove: fn(&mut EntityWorldMut),
@ -93,15 +93,9 @@ impl ReflectBundle {
bundle: &dyn PartialReflect,
registry: &TypeRegistry,
mapper: &mut dyn EntityMapper,
relationship_insert_hook_mode: RelationshipInsertHookMode,
relationship_hook_mode: RelationshipHookMode,
) {
(self.0.apply_or_insert_mapped)(
entity,
bundle,
registry,
mapper,
relationship_insert_hook_mode,
);
(self.0.apply_or_insert_mapped)(entity, bundle, registry, mapper, relationship_hook_mode);
}
/// Removes this [`Bundle`] type from the entity. Does nothing if it doesn't exist.
@ -183,46 +177,49 @@ impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for Refl
}
}
},
apply_or_insert_mapped:
|entity, reflected_bundle, registry, mapper, relationship_insert_hook_mode| {
if let Some(reflect_component) =
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
{
reflect_component.apply_or_insert_mapped(
entity,
reflected_bundle,
registry,
mapper,
relationship_insert_hook_mode,
);
} else {
match reflected_bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle.iter_fields().for_each(|field| {
apply_or_insert_field_mapped(
entity,
field,
registry,
mapper,
relationship_insert_hook_mode,
);
}),
ReflectRef::Tuple(bundle) => bundle.iter_fields().for_each(|field| {
apply_or_insert_field_mapped(
entity,
field,
registry,
mapper,
relationship_insert_hook_mode,
);
}),
_ => panic!(
"expected bundle `{}` to be a named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
core::any::type_name::<B>(),
),
}
apply_or_insert_mapped: |entity,
reflected_bundle,
registry,
mapper,
relationship_hook_mode| {
if let Some(reflect_component) =
registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
{
reflect_component.apply_or_insert_mapped(
entity,
reflected_bundle,
registry,
mapper,
relationship_hook_mode,
);
} else {
match reflected_bundle.reflect_ref() {
ReflectRef::Struct(bundle) => bundle.iter_fields().for_each(|field| {
apply_or_insert_field_mapped(
entity,
field,
registry,
mapper,
relationship_hook_mode,
);
}),
ReflectRef::Tuple(bundle) => bundle.iter_fields().for_each(|field| {
apply_or_insert_field_mapped(
entity,
field,
registry,
mapper,
relationship_hook_mode,
);
}),
_ => panic!(
"expected bundle `{}` to be a named struct or tuple",
// FIXME: once we have unique reflect, use `TypePath`.
core::any::type_name::<B>(),
),
}
},
}
},
remove: |entity| {
entity.remove::<B>();
},
@ -259,7 +256,7 @@ fn apply_or_insert_field_mapped(
field: &dyn PartialReflect,
registry: &TypeRegistry,
mapper: &mut dyn EntityMapper,
relationship_insert_hook_mode: RelationshipInsertHookMode,
relationship_hook_mode: RelationshipHookMode,
) {
let Some(type_id) = field.try_as_reflect().map(Any::type_id) else {
panic!(
@ -274,7 +271,7 @@ fn apply_or_insert_field_mapped(
field,
registry,
mapper,
relationship_insert_hook_mode,
relationship_hook_mode,
);
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(type_id) {
reflect_bundle.apply_or_insert_mapped(
@ -282,7 +279,7 @@ fn apply_or_insert_field_mapped(
field,
registry,
mapper,
relationship_insert_hook_mode,
relationship_hook_mode,
);
} else {
let is_component = entity.world().components().get_id(type_id).is_some();

View File

@ -63,7 +63,7 @@ use crate::{
component::{ComponentId, ComponentMutability},
entity::{Entity, EntityMapper},
prelude::Component,
relationship::RelationshipInsertHookMode,
relationship::RelationshipHookMode,
world::{
unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityWorldMut, FilteredEntityMut,
FilteredEntityRef, World,
@ -111,7 +111,7 @@ pub struct ReflectComponentFns {
&dyn PartialReflect,
&TypeRegistry,
&mut dyn EntityMapper,
RelationshipInsertHookMode,
RelationshipHookMode,
),
/// Function pointer implementing [`ReflectComponent::remove()`].
pub remove: fn(&mut EntityWorldMut),
@ -180,15 +180,9 @@ impl ReflectComponent {
component: &dyn PartialReflect,
registry: &TypeRegistry,
map: &mut dyn EntityMapper,
relationship_insert_hook_mode: RelationshipInsertHookMode,
relationship_hook_mode: RelationshipHookMode,
) {
(self.0.apply_or_insert_mapped)(
entity,
component,
registry,
map,
relationship_insert_hook_mode,
);
(self.0.apply_or_insert_mapped)(entity, component, registry, map, relationship_hook_mode);
}
/// Removes this [`Component`] type from the entity. Does nothing if it doesn't exist.
@ -333,40 +327,33 @@ impl<C: Component + Reflect + TypePath> FromType<C> for ReflectComponent {
let mut component = unsafe { entity.get_mut_assume_mutable::<C>() }.unwrap();
component.apply(reflected_component);
},
apply_or_insert_mapped:
|entity, reflected_component, registry, mapper, relationship_insert_hook_mode| {
let map_fn = map_function(mapper);
if C::Mutability::MUTABLE {
// SAFETY: guard ensures `C` is a mutable component
if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::<C>() }
{
component.apply(reflected_component.as_partial_reflect());
C::visit_entities_mut(&mut component, map_fn);
} else {
let mut component = entity.world_scope(|world| {
from_reflect_with_fallback::<C>(
reflected_component,
world,
registry,
)
});
C::visit_entities_mut(&mut component, map_fn);
entity.insert_with_relationship_insert_hook_mode(
component,
relationship_insert_hook_mode,
);
}
apply_or_insert_mapped: |entity,
reflected_component,
registry,
mapper,
relationship_hook_mode| {
let map_fn = map_function(mapper);
if C::Mutability::MUTABLE {
// SAFETY: guard ensures `C` is a mutable component
if let Some(mut component) = unsafe { entity.get_mut_assume_mutable::<C>() } {
component.apply(reflected_component.as_partial_reflect());
C::visit_entities_mut(&mut component, map_fn);
} else {
let mut component = entity.world_scope(|world| {
from_reflect_with_fallback::<C>(reflected_component, world, registry)
});
C::visit_entities_mut(&mut component, map_fn);
entity.insert_with_relationship_insert_hook_mode(
component,
relationship_insert_hook_mode,
);
entity
.insert_with_relationship_hook_mode(component, relationship_hook_mode);
}
},
} else {
let mut component = entity.world_scope(|world| {
from_reflect_with_fallback::<C>(reflected_component, world, registry)
});
C::visit_entities_mut(&mut component, map_fn);
entity.insert_with_relationship_hook_mode(component, relationship_hook_mode);
}
},
remove: |entity| {
entity.remove::<C>();
},

View File

@ -82,7 +82,7 @@ pub trait ReflectCommandExt {
/// // use the insert_reflect entity command to insert that component/bundle into an entity.
/// commands
/// .spawn_empty()
/// .insert_reflect(prefab.data.clone_value());
/// .insert_reflect(prefab.data.reflect_clone().unwrap().into_partial_reflect());
/// }
/// ```
fn insert_reflect(&mut self, component: Box<dyn PartialReflect>) -> &mut Self;
@ -442,21 +442,30 @@ mod tests {
let entity = commands.spawn_empty().id();
let entity2 = commands.spawn_empty().id();
let entity3 = commands.spawn_empty().id();
let boxed_reflect_component_a = Box::new(ComponentA(916)) as Box<dyn PartialReflect>;
let boxed_reflect_component_a_clone = boxed_reflect_component_a.clone_value();
let boxed_reflect_component_a_clone = boxed_reflect_component_a.reflect_clone().unwrap();
let boxed_reflect_component_a_dynamic = boxed_reflect_component_a.to_dynamic();
commands
.entity(entity)
.insert_reflect(boxed_reflect_component_a);
commands
.entity(entity2)
.insert_reflect(boxed_reflect_component_a_clone);
.insert_reflect(boxed_reflect_component_a_clone.into_partial_reflect());
commands
.entity(entity3)
.insert_reflect(boxed_reflect_component_a_dynamic);
system_state.apply(&mut world);
assert_eq!(
world.entity(entity).get::<ComponentA>(),
world.entity(entity2).get::<ComponentA>()
world.entity(entity2).get::<ComponentA>(),
);
assert_eq!(
world.entity(entity).get::<ComponentA>(),
world.entity(entity3).get::<ComponentA>(),
);
}

View File

@ -8,7 +8,10 @@ use crate::{
change_detection::Mut,
component::ComponentId,
resource::Resource,
world::{unsafe_world_cell::UnsafeWorldCell, FilteredResources, FilteredResourcesMut, World},
world::{
error::ResourceFetchError, unsafe_world_cell::UnsafeWorldCell, FilteredResources,
FilteredResourcesMut, World,
},
};
use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry};
@ -52,9 +55,12 @@ pub struct ReflectResourceFns {
/// Function pointer implementing [`ReflectResource::remove()`].
pub remove: fn(&mut World),
/// Function pointer implementing [`ReflectResource::reflect()`].
pub reflect: for<'w> fn(FilteredResources<'w, '_>) -> Option<&'w dyn Reflect>,
pub reflect:
for<'w> fn(FilteredResources<'w, '_>) -> Result<&'w dyn Reflect, ResourceFetchError>,
/// Function pointer implementing [`ReflectResource::reflect_mut()`].
pub reflect_mut: for<'w> fn(FilteredResourcesMut<'w, '_>) -> Option<Mut<'w, dyn Reflect>>,
pub reflect_mut: for<'w> fn(
FilteredResourcesMut<'w, '_>,
) -> Result<Mut<'w, dyn Reflect>, ResourceFetchError>,
/// Function pointer implementing [`ReflectResource::reflect_unchecked_mut()`].
///
/// # Safety
@ -118,7 +124,7 @@ impl ReflectResource {
pub fn reflect<'w, 's>(
&self,
resources: impl Into<FilteredResources<'w, 's>>,
) -> Option<&'w dyn Reflect> {
) -> Result<&'w dyn Reflect, ResourceFetchError> {
(self.0.reflect)(resources.into())
}
@ -128,7 +134,7 @@ impl ReflectResource {
pub fn reflect_mut<'w, 's>(
&self,
resources: impl Into<FilteredResourcesMut<'w, 's>>,
) -> Option<Mut<'w, dyn Reflect>> {
) -> Result<Mut<'w, dyn Reflect>, ResourceFetchError> {
(self.0.reflect_mut)(resources.into())
}

View File

@ -13,10 +13,10 @@ pub use relationship_source_collection::*;
use crate::{
component::{Component, HookContext, Mutable},
entity::{ComponentCloneCtx, Entity, SourceComponent},
error::{ignore, CommandWithEntity, HandleError},
system::{
command::HandleError,
entity_command::{self, CommandWithEntity},
error_handler, Commands,
entity_command::{self},
Commands,
},
world::{DeferredWorld, EntityWorldMut},
};
@ -90,14 +90,14 @@ pub trait Relationship: Component + Sized {
HookContext {
entity,
caller,
relationship_insert_hook_mode,
relationship_hook_mode,
..
}: HookContext,
) {
match relationship_insert_hook_mode {
RelationshipInsertHookMode::Run => {}
RelationshipInsertHookMode::Skip => return,
RelationshipInsertHookMode::RunIfNotLinked => {
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
return;
}
@ -137,7 +137,23 @@ pub trait Relationship: Component + Sized {
/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
// note: think of this as "on_drop"
fn on_replace(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
fn on_replace(
mut world: DeferredWorld,
HookContext {
entity,
relationship_hook_mode,
..
}: HookContext,
) {
match relationship_hook_mode {
RelationshipHookMode::Run => {}
RelationshipHookMode::Skip => return,
RelationshipHookMode::RunIfNotLinked => {
if <Self::RelationshipTarget as RelationshipTarget>::LINKED_SPAWN {
return;
}
}
}
let target_entity = world.entity(entity).get::<Self>().unwrap().get();
if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) {
if let Some(mut relationship_target) =
@ -207,29 +223,23 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
// note: think of this as "on_drop"
fn on_replace(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
// NOTE: this unsafe code is an optimization. We could make this safe, but it would require
// copying the RelationshipTarget collection
// SAFETY: This only reads the Self component and queues Remove commands
unsafe {
let world = world.as_unsafe_world_cell();
let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap();
let mut commands = world.get_raw_command_queue();
for source_entity in relationship_target.iter() {
if world.get_entity(source_entity).is_ok() {
commands.push(
entity_command::remove::<Self::Relationship>()
.with_entity(source_entity)
.handle_error_with(error_handler::silent()),
);
} else {
warn!(
"{}Tried to despawn non-existent entity {}",
caller
.map(|location| format!("{location}: "))
.unwrap_or_default(),
source_entity
);
}
let (entities, mut commands) = world.entities_and_commands();
let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
for source_entity in relationship_target.iter() {
if entities.get(source_entity).is_ok() {
commands.queue(
entity_command::remove::<Self::Relationship>()
.with_entity(source_entity)
.handle_error_with(ignore),
);
} else {
warn!(
"{}Tried to despawn non-existent entity {}",
caller
.map(|location| format!("{location}: "))
.unwrap_or_default(),
source_entity
);
}
}
}
@ -238,29 +248,23 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
/// that entity is despawned.
// note: think of this as "on_drop"
fn on_despawn(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
// NOTE: this unsafe code is an optimization. We could make this safe, but it would require
// copying the RelationshipTarget collection
// SAFETY: This only reads the Self component and queues despawn commands
unsafe {
let world = world.as_unsafe_world_cell();
let relationship_target = world.get_entity(entity).unwrap().get::<Self>().unwrap();
let mut commands = world.get_raw_command_queue();
for source_entity in relationship_target.iter() {
if world.get_entity(source_entity).is_ok() {
commands.push(
entity_command::despawn()
.with_entity(source_entity)
.handle_error_with(error_handler::silent()),
);
} else {
warn!(
"{}Tried to despawn non-existent entity {}",
caller
.map(|location| format!("{location}: "))
.unwrap_or_default(),
source_entity
);
}
let (entities, mut commands) = world.entities_and_commands();
let relationship_target = entities.get(entity).unwrap().get::<Self>().unwrap();
for source_entity in relationship_target.iter() {
if entities.get(source_entity).is_ok() {
commands.queue(
entity_command::despawn()
.with_entity(source_entity)
.handle_error_with(ignore),
);
} else {
warn!(
"{}Tried to despawn non-existent entity {}",
caller
.map(|location| format!("{location}: "))
.unwrap_or_default(),
source_entity
);
}
}
}
@ -317,14 +321,14 @@ pub fn clone_relationship_target<T: RelationshipTarget>(
}
}
/// Configures the conditions under which the Relationship insert hook will be run.
/// Configures the conditions under which the Relationship insert/replace hooks will be run.
#[derive(Copy, Clone, Debug)]
pub enum RelationshipInsertHookMode {
/// Relationship insert hooks will always run
pub enum RelationshipHookMode {
/// Relationship insert/replace hooks will always run
Run,
/// Relationship insert hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false
/// Relationship insert/replace hooks will run if [`RelationshipTarget::LINKED_SPAWN`] is false
RunIfNotLinked,
/// Relationship insert hooks will always be skipped
/// Relationship insert/replace hooks will always be skipped
Skip,
}

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