Merge branch 'main' into reflect-auto-registration-staticlib
This commit is contained in:
commit
eb66a7bcfc
31
.github/workflows/ci.yml
vendored
31
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
||||
60
Cargo.toml
60
Cargo.toml
@ -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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import super::shaders::util::make_polka_dots;
|
||||
import super::util::make_polka_dots;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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), ());
|
||||
},
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
///
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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,
|
||||
|
||||
40
crates/bevy_anti_aliasing/Cargo.toml
Normal file
40
crates/bevy_anti_aliasing/Cargo.toml
Normal 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
|
||||
176
crates/bevy_anti_aliasing/LICENSE-APACHE
Normal file
176
crates/bevy_anti_aliasing/LICENSE-APACHE
Normal 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
|
||||
19
crates/bevy_anti_aliasing/LICENSE-MIT
Normal file
19
crates/bevy_anti_aliasing/LICENSE-MIT
Normal 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.
|
||||
7
crates/bevy_anti_aliasing/README.md
Normal file
7
crates/bevy_anti_aliasing/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Bevy Anti Aliasing
|
||||
|
||||
[](https://github.com/bevyengine/bevy#license)
|
||||
[](https://crates.io/crates/bevy_core_pipeline)
|
||||
[](https://crates.io/crates/bevy_core_pipeline)
|
||||
[](https://docs.rs/bevy_core_pipeline/latest/bevy_core_pipeline/)
|
||||
[](https://discord.gg/bevy)
|
||||
@ -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`].
|
||||
9
crates/bevy_anti_aliasing/src/experimental/mod.rs
Normal file
9
crates/bevy_anti_aliasing/src/experimental/mod.rs
Normal 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};
|
||||
}
|
||||
@ -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 {
|
||||
27
crates/bevy_anti_aliasing/src/lib.rs
Normal file
27
crates/bevy_anti_aliasing/src/lib.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
@ -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 {
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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`]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
})?;
|
||||
|
||||
@ -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(),
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
///
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
///
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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)
|
||||
///
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -5,7 +5,3 @@
|
||||
//! are included nonetheless for testing purposes.
|
||||
|
||||
pub mod mip_generation;
|
||||
|
||||
pub mod taa {
|
||||
pub use crate::taa::{TemporalAntiAliasNode, TemporalAntiAliasPlugin, TemporalAntiAliasing};
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
///
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
})
|
||||
|
||||
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>>;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
108
crates/bevy_ecs/src/error/command_handling.rs
Normal file
108
crates/bevy_ecs/src/error/command_handling.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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) {}
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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>,
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))]
|
||||
|
||||
@ -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"),
|
||||
|
||||
@ -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> =
|
||||
|
||||
@ -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`].
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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>();
|
||||
},
|
||||
|
||||
@ -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>(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user