mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 14:40:19 +00:00
Merge branch 'main' into transmission
This commit is contained in:
commit
32e9a9cc62
247 changed files with 4074 additions and 1948 deletions
8
.github/workflows/daily.yml
vendored
8
.github/workflows/daily.yml
vendored
|
@ -89,6 +89,14 @@ jobs:
|
|||
include:
|
||||
- device: "iPhone 13"
|
||||
os_version: "15"
|
||||
- device: "iPhone 14"
|
||||
os_version: "16"
|
||||
- device: "iPhone 15"
|
||||
os_version: "17"
|
||||
- device: "Xiaomi Redmi Note 11"
|
||||
os_version: "11.0"
|
||||
- device: "Google Pixel 6"
|
||||
os_version: "12.0"
|
||||
- device: "Samsung Galaxy S23"
|
||||
os_version: "13.0"
|
||||
steps:
|
||||
|
|
|
@ -199,6 +199,9 @@ serialize = ["bevy_internal/serialize"]
|
|||
# Enables multithreaded parallelism in the engine. Disabling it forces all engine tasks to run on a single thread.
|
||||
multi-threaded = ["bevy_internal/multi-threaded"]
|
||||
|
||||
# Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.
|
||||
async-io = ["bevy_internal/async-io"]
|
||||
|
||||
# Wayland display server support
|
||||
wayland = ["bevy_internal/wayland"]
|
||||
|
||||
|
@ -252,7 +255,6 @@ bevy_dylib = { path = "crates/bevy_dylib", version = "0.12.0-dev", default-featu
|
|||
bevy_internal = { path = "crates/bevy_internal", version = "0.12.0-dev", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.4"
|
||||
rand = "0.8.0"
|
||||
ron = "0.8.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
|
||||
[![Crates.io](https://img.shields.io/crates/d/bevy.svg)](https://crates.io/crates/bevy)
|
||||
[![Rust](https://github.com/bevyengine/bevy/workflows/CI/badge.svg)](https://github.com/bevyengine/bevy/actions)
|
||||
![iOS cron CI](https://github.com/bevyengine/bevy/workflows/iOS%20cron%20CI/badge.svg)
|
||||
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)
|
||||
|
||||
## What is Bevy?
|
||||
|
|
File diff suppressed because one or more lines are too long
110
assets/models/torus/torus.gltf
Normal file
110
assets/models/torus/torus.gltf
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,4 +1,6 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
// we can import items from shader modules in the assets folder with a quoted path
|
||||
#import "shaders/custom_material_import.wgsl" COLOR_MULTIPLIER
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
|
@ -12,5 +14,5 @@ struct CustomMaterial {
|
|||
fn fragment(
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv);
|
||||
return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv) * COLOR_MULTIPLIER;
|
||||
}
|
||||
|
|
2
assets/shaders/custom_material_import.wgsl
Normal file
2
assets/shaders/custom_material_import.wgsl
Normal file
|
@ -0,0 +1,2 @@
|
|||
// this is made available to the importing module
|
||||
const COLOR_MULTIPLIER: vec4<f32> = vec4<f32>(1.0, 1.0, 1.0, 0.5);
|
|
@ -27,6 +27,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut data in self.1.iter_mut(&mut self.0) {
|
||||
data.0 *= 2.0;
|
||||
|
|
|
@ -27,6 +27,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut data| {
|
||||
data.0 *= 2.0;
|
||||
|
|
|
@ -38,6 +38,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut data| {
|
||||
data.0 *= 2.0;
|
||||
|
|
|
@ -55,6 +55,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut data| {
|
||||
data.0 .0 *= 2.0;
|
||||
|
|
|
@ -65,6 +65,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut data| {
|
||||
data.0 .0 *= 2.0;
|
||||
|
|
|
@ -38,6 +38,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut data in self.1.iter_mut(&mut self.0) {
|
||||
data.0 *= 2.0;
|
||||
|
|
|
@ -55,6 +55,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut data in self.1.iter_mut(&mut self.0) {
|
||||
data.0 .0 *= 2.0;
|
||||
|
|
|
@ -65,6 +65,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut data in self.1.iter_mut(&mut self.0) {
|
||||
data.0 .0 *= 2.0;
|
||||
|
|
|
@ -33,6 +33,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for (velocity, mut position) in self.1.iter_mut(&mut self.0) {
|
||||
position.0 += velocity.0;
|
||||
|
|
|
@ -33,6 +33,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1
|
||||
.for_each_mut(&mut self.0, |(velocity, mut position)| {
|
||||
|
|
|
@ -35,6 +35,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1
|
||||
.for_each_mut(&mut self.0, |(velocity, mut position)| {
|
||||
|
|
|
@ -55,6 +55,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut item| {
|
||||
item.1 .0 += item.0 .0;
|
||||
|
|
|
@ -57,6 +57,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.for_each_mut(&mut self.0, |mut item| {
|
||||
item.1 .0 += item.0 .0;
|
||||
|
|
|
@ -35,6 +35,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for (velocity, mut position) in self.1.iter_mut(&mut self.0) {
|
||||
position.0 += velocity.0;
|
||||
|
|
|
@ -41,6 +41,7 @@ impl Benchmark {
|
|||
Self(world, Box::new(system))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
self.1.run((), &mut self.0);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut item in self.1.iter_mut(&mut self.0) {
|
||||
item.1 .0 += item.0 .0;
|
||||
|
|
|
@ -57,6 +57,7 @@ impl<'w> Benchmark<'w> {
|
|||
Self(world, query)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn run(&mut self) {
|
||||
for mut item in self.1.iter_mut(&mut self.0) {
|
||||
item.1 .0 += item.0 .0;
|
||||
|
|
|
@ -1 +1 @@
|
|||
doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGPU"]
|
||||
doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGPU", "GilRs"]
|
||||
|
|
|
@ -14,4 +14,4 @@ bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
|||
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
|
||||
accesskit = "0.11"
|
||||
accesskit = "0.12"
|
||||
|
|
|
@ -4,17 +4,18 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use std::{
|
||||
num::NonZeroU128,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
pub use accesskit;
|
||||
use accesskit::{NodeBuilder, NodeId};
|
||||
use accesskit::NodeBuilder;
|
||||
use bevy_app::Plugin;
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
prelude::{Component, Entity, Event},
|
||||
schedule::SystemSet,
|
||||
system::Resource,
|
||||
};
|
||||
|
||||
|
@ -30,6 +31,46 @@ pub struct ActionRequest(pub accesskit::ActionRequest);
|
|||
#[derive(Resource, Default, Clone, Debug, Deref, DerefMut)]
|
||||
pub struct AccessibilityRequested(Arc<AtomicBool>);
|
||||
|
||||
impl AccessibilityRequested {
|
||||
/// Returns `true` if an access technology is active and accessibility tree
|
||||
/// updates should be sent.
|
||||
pub fn get(&self) -> bool {
|
||||
self.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Sets whether accessibility updates were requested by an access technology.
|
||||
pub fn set(&self, value: bool) {
|
||||
self.store(value, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource whose value determines whether the accessibility tree is updated
|
||||
/// via the ECS.
|
||||
///
|
||||
/// Set to `false` in cases where an external GUI library is sending
|
||||
/// accessibility updates instead. Without this, the external library and ECS
|
||||
/// will generate conflicting updates.
|
||||
#[derive(Resource, Clone, Debug, Deref, DerefMut)]
|
||||
pub struct ManageAccessibilityUpdates(bool);
|
||||
|
||||
impl Default for ManageAccessibilityUpdates {
|
||||
fn default() -> Self {
|
||||
Self(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl ManageAccessibilityUpdates {
|
||||
/// Returns `true` if the ECS should update the accessibility tree.
|
||||
pub fn get(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Sets whether the ECS should update the accessibility tree.
|
||||
pub fn set(&mut self, value: bool) {
|
||||
self.0 = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Component to wrap a [`accesskit::Node`], representing this entity to the platform's
|
||||
/// accessibility API.
|
||||
///
|
||||
|
@ -47,22 +88,16 @@ impl From<NodeBuilder> for AccessibilityNode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Extensions to ease integrating entities with [`AccessKit`](https://accesskit.dev).
|
||||
pub trait AccessKitEntityExt {
|
||||
/// Convert an entity to a stable [`NodeId`].
|
||||
fn to_node_id(&self) -> NodeId;
|
||||
}
|
||||
|
||||
impl AccessKitEntityExt for Entity {
|
||||
fn to_node_id(&self) -> NodeId {
|
||||
let id = NonZeroU128::new(self.to_bits() as u128 + 1);
|
||||
NodeId(id.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Resource representing which entity has keyboard focus, if any.
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct Focus(Option<Entity>);
|
||||
pub struct Focus(pub Option<Entity>);
|
||||
|
||||
/// Set enum for the systems relating to accessibility
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||
pub enum AccessibilitySystem {
|
||||
/// Update the accessibility tree
|
||||
Update,
|
||||
}
|
||||
|
||||
/// Plugin managing non-GUI aspects of integrating with accessibility APIs.
|
||||
pub struct AccessibilityPlugin;
|
||||
|
@ -70,6 +105,7 @@ pub struct AccessibilityPlugin;
|
|||
impl Plugin for AccessibilityPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.init_resource::<AccessibilityRequested>()
|
||||
.init_resource::<ManageAccessibilityUpdates>()
|
||||
.init_resource::<Focus>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -453,16 +453,16 @@ fn entity_from_path(
|
|||
/// Verify that there are no ancestors of a given entity that have an [`AnimationPlayer`].
|
||||
fn verify_no_ancestor_player(
|
||||
player_parent: Option<&Parent>,
|
||||
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
parents: &Query<(Has<AnimationPlayer>, Option<&Parent>)>,
|
||||
) -> bool {
|
||||
let Some(mut current) = player_parent.map(Parent::get) else {
|
||||
return true;
|
||||
};
|
||||
loop {
|
||||
let Ok((maybe_player, parent)) = parents.get(current) else {
|
||||
let Ok((has_player, parent)) = parents.get(current) else {
|
||||
return true;
|
||||
};
|
||||
if maybe_player.is_some() {
|
||||
if has_player {
|
||||
return false;
|
||||
}
|
||||
if let Some(parent) = parent {
|
||||
|
@ -483,7 +483,7 @@ pub fn animation_player(
|
|||
names: Query<&Name>,
|
||||
transforms: Query<&mut Transform>,
|
||||
morphs: Query<&mut MorphWeights>,
|
||||
parents: Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
parents: Query<(Has<AnimationPlayer>, Option<&Parent>)>,
|
||||
mut animation_players: Query<(Entity, Option<&Parent>, &mut AnimationPlayer)>,
|
||||
) {
|
||||
animation_players
|
||||
|
@ -515,7 +515,7 @@ fn run_animation_player(
|
|||
transforms: &Query<&mut Transform>,
|
||||
morphs: &Query<&mut MorphWeights>,
|
||||
maybe_parent: Option<&Parent>,
|
||||
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
parents: &Query<(Has<AnimationPlayer>, Option<&Parent>)>,
|
||||
children: &Query<&Children>,
|
||||
) {
|
||||
let paused = player.paused;
|
||||
|
@ -565,8 +565,16 @@ fn run_animation_player(
|
|||
}
|
||||
}
|
||||
|
||||
/// Update `weights` based on weights in `keyframes` at index `key_index`
|
||||
/// with a linear interpolation on `key_lerp`.
|
||||
/// Update `weights` based on weights in `keyframe` with a linear interpolation
|
||||
/// on `key_lerp`.
|
||||
fn lerp_morph_weights(weights: &mut [f32], keyframe: impl Iterator<Item = f32>, key_lerp: f32) {
|
||||
let zipped = weights.iter_mut().zip(keyframe);
|
||||
for (morph_weight, keyframe) in zipped {
|
||||
*morph_weight += (keyframe - *morph_weight) * key_lerp;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a keyframe from a list of keyframes by index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -575,16 +583,10 @@ fn run_animation_player(
|
|||
/// This happens when `keyframes` is not formatted as described in
|
||||
/// [`Keyframes::Weights`]. A possible cause is [`AnimationClip`] not being
|
||||
/// meant to be used for the [`MorphWeights`] of the entity it's being applied to.
|
||||
fn lerp_morph_weights(weights: &mut [f32], key_lerp: f32, keyframes: &[f32], key_index: usize) {
|
||||
let target_count = weights.len();
|
||||
fn get_keyframe(target_count: usize, keyframes: &[f32], key_index: usize) -> &[f32] {
|
||||
let start = target_count * key_index;
|
||||
let end = target_count * (key_index + 1);
|
||||
|
||||
let zipped = weights.iter_mut().zip(&keyframes[start..end]);
|
||||
for (morph_weight, keyframe) in zipped {
|
||||
let minus_lerp = 1.0 - key_lerp;
|
||||
*morph_weight = (*morph_weight * minus_lerp) + (keyframe * key_lerp);
|
||||
}
|
||||
&keyframes[start..end]
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -599,7 +601,7 @@ fn apply_animation(
|
|||
transforms: &Query<&mut Transform>,
|
||||
morphs: &Query<&mut MorphWeights>,
|
||||
maybe_parent: Option<&Parent>,
|
||||
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
parents: &Query<(Has<AnimationPlayer>, Option<&Parent>)>,
|
||||
children: &Query<&Children>,
|
||||
) {
|
||||
if let Some(animation_clip) = animations.get(&animation.animation_clip) {
|
||||
|
@ -657,7 +659,12 @@ fn apply_animation(
|
|||
}
|
||||
Keyframes::Weights(keyframes) => {
|
||||
if let Ok(morphs) = &mut morphs {
|
||||
lerp_morph_weights(morphs.weights_mut(), weight, keyframes, 0);
|
||||
let target_count = morphs.weights().len();
|
||||
lerp_morph_weights(
|
||||
morphs.weights_mut(),
|
||||
get_keyframe(target_count, keyframes, 0).iter().copied(),
|
||||
weight,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -707,7 +714,14 @@ fn apply_animation(
|
|||
}
|
||||
Keyframes::Weights(keyframes) => {
|
||||
if let Ok(morphs) = &mut morphs {
|
||||
lerp_morph_weights(morphs.weights_mut(), weight, keyframes, step_start);
|
||||
let target_count = morphs.weights().len();
|
||||
let morph_start = get_keyframe(target_count, keyframes, step_start);
|
||||
let morph_end = get_keyframe(target_count, keyframes, step_start + 1);
|
||||
let result = morph_start
|
||||
.iter()
|
||||
.zip(morph_end)
|
||||
.map(|(a, b)| *a + lerp * (*b - *a));
|
||||
lerp_morph_weights(morphs.weights_mut(), result, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -334,7 +334,7 @@ impl App {
|
|||
/// initial state.
|
||||
///
|
||||
/// If you would like to control how other systems run based on the current state,
|
||||
/// you can emulate this behavior using the [`in_state`] [`Condition`](bevy_ecs::schedule::Condition).
|
||||
/// you can emulate this behavior using the [`in_state`] [`Condition`].
|
||||
///
|
||||
/// Note that you can also apply state transitions at other points in the schedule
|
||||
/// by adding the [`apply_state_transition`] system manually.
|
||||
|
@ -859,6 +859,83 @@ impl App {
|
|||
.configure_schedules(schedule_build_settings);
|
||||
self
|
||||
}
|
||||
|
||||
/// When doing [ambiguity checking](bevy_ecs::schedule::ScheduleBuildSettings) this
|
||||
/// ignores systems that are ambiguious on [`Component`] T.
|
||||
///
|
||||
/// This settings only applies to the main world. To apply this to other worlds call the
|
||||
/// [corresponding method](World::allow_ambiguous_component) on World
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings};
|
||||
/// # use bevy_utils::default;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct A;
|
||||
///
|
||||
/// // these systems are ambiguous on A
|
||||
/// fn system_1(_: Query<&mut A>) {}
|
||||
/// fn system_2(_: Query<&A>) {}
|
||||
///
|
||||
/// let mut app = App::new();
|
||||
/// app.configure_schedules(ScheduleBuildSettings {
|
||||
/// ambiguity_detection: LogLevel::Error,
|
||||
/// ..default()
|
||||
/// });
|
||||
///
|
||||
/// app.add_systems(Update, ( system_1, system_2 ));
|
||||
/// app.allow_ambiguous_component::<A>();
|
||||
///
|
||||
/// // running the app does not error.
|
||||
/// app.update();
|
||||
/// ```
|
||||
pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
|
||||
self.world.allow_ambiguous_component::<T>();
|
||||
self
|
||||
}
|
||||
|
||||
/// When doing [ambiguity checking](bevy_ecs::schedule::ScheduleBuildSettings) this
|
||||
/// ignores systems that are ambiguious on [`Resource`] T.
|
||||
///
|
||||
/// This settings only applies to the main world. To apply this to other worlds call the
|
||||
/// [corresponding method](World::allow_ambiguous_resource) on World
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::schedule::{LogLevel, ScheduleBuildSettings};
|
||||
/// # use bevy_utils::default;
|
||||
///
|
||||
/// #[derive(Resource)]
|
||||
/// struct R;
|
||||
///
|
||||
/// // these systems are ambiguous on R
|
||||
/// fn system_1(_: ResMut<R>) {}
|
||||
/// fn system_2(_: Res<R>) {}
|
||||
///
|
||||
/// let mut app = App::new();
|
||||
/// app.configure_schedules(ScheduleBuildSettings {
|
||||
/// ambiguity_detection: LogLevel::Error,
|
||||
/// ..default()
|
||||
/// });
|
||||
/// app.insert_resource(R);
|
||||
///
|
||||
/// app.add_systems(Update, ( system_1, system_2 ));
|
||||
/// app.allow_ambiguous_resource::<R>();
|
||||
///
|
||||
/// // running the app does not error.
|
||||
/// app.update();
|
||||
/// ```
|
||||
pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
|
||||
self.world.allow_ambiguous_resource::<T>();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn run_once(mut app: App) {
|
||||
|
|
|
@ -51,7 +51,7 @@ pub trait Plugin: Downcast + Any + Send + Sync {
|
|||
std::any::type_name::<Self>()
|
||||
}
|
||||
|
||||
/// If the plugin can be meaningfully instantiated several times in an [`App`](crate::App),
|
||||
/// If the plugin can be meaningfully instantiated several times in an [`App`],
|
||||
/// override this method to return `false`.
|
||||
fn is_unique(&self) -> bool {
|
||||
true
|
||||
|
|
|
@ -23,7 +23,6 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev" }
|
|||
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
anyhow = "1.0"
|
||||
async-broadcast = "0.5"
|
||||
async-fs = "1.5"
|
||||
async-lock = "2.8"
|
||||
|
@ -36,7 +35,7 @@ parking_lot = { version = "0.12", features = ["arc_lock", "send_guard"] }
|
|||
ron = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
notify-debouncer-full = { version = "0.2.0", optional = true }
|
||||
notify-debouncer-full = { version = "0.3.1", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
bevy_winit = { path = "../bevy_winit", version = "0.12.0-dev" }
|
||||
|
|
|
@ -66,9 +66,16 @@ fn derive_dependency_visitor_internal(
|
|||
let struct_name = &ast.ident;
|
||||
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
||||
|
||||
// prevent unused variable warning in case there are no dependencies
|
||||
let visit = if field_visitors.is_empty() {
|
||||
quote! { _visit }
|
||||
} else {
|
||||
quote! { visit }
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics #bevy_asset_path::VisitAssetDependencies for #struct_name #type_generics #where_clause {
|
||||
fn visit_dependencies(&self, visit: &mut impl FnMut(#bevy_asset_path::UntypedAssetId)) {
|
||||
fn visit_dependencies(&self, #visit: &mut impl FnMut(#bevy_asset_path::UntypedAssetId)) {
|
||||
#(#field_visitors)*
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::io::{
|
|||
get_meta_path, AssetReader, AssetReaderError, AssetWatcher, EmptyPathStream, PathStream,
|
||||
Reader, VecReader,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use bevy_log::error;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use std::{ffi::CString, path::Path};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::io::{AssetSourceEvent, AssetWatcher};
|
||||
use anyhow::Result;
|
||||
use bevy_log::error;
|
||||
use bevy_utils::Duration;
|
||||
use crossbeam_channel::Sender;
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::io::{
|
|||
get_meta_path, AssetReader, AssetReaderError, AssetWatcher, AssetWriter, AssetWriterError,
|
||||
PathStream, Reader, Writer,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_fs::{read_dir, File};
|
||||
use bevy_utils::BoxedFuture;
|
||||
use futures_lite::StreamExt;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
|
||||
use anyhow::Result;
|
||||
use bevy_utils::{BoxedFuture, HashMap};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use parking_lot::RwLock;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
|
||||
use anyhow::Result;
|
||||
use bevy_utils::{BoxedFuture, HashMap};
|
||||
use futures_io::AsyncRead;
|
||||
use futures_lite::{ready, Stream};
|
||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
|||
processor::{AssetProcessorData, ProcessStatus},
|
||||
AssetPath,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use async_lock::RwLockReadGuardArc;
|
||||
use bevy_log::trace;
|
||||
use bevy_utils::BoxedFuture;
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::io::{
|
|||
get_meta_path, AssetReader, AssetReaderError, AssetWatcher, EmptyPathStream, PathStream,
|
||||
Reader, VecReader,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use bevy_log::error;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use js_sys::{Uint8Array, JSON};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
|
||||
pub mod io;
|
||||
pub mod meta;
|
||||
pub mod processor;
|
||||
|
@ -33,7 +35,6 @@ pub use path::*;
|
|||
pub use reflect::*;
|
||||
pub use server::*;
|
||||
|
||||
pub use anyhow;
|
||||
pub use bevy_utils::BoxedFuture;
|
||||
|
||||
use crate::{
|
||||
|
@ -261,6 +262,9 @@ pub trait AssetApp {
|
|||
/// * Registering the [`Asset`] in the [`AssetServer`]
|
||||
/// * Initializing the [`AssetEvent`] resource for the [`Asset`]
|
||||
/// * Adding other relevant systems and resources for the [`Asset`]
|
||||
/// * Ignoring schedule ambiguities in [`Assets`] resource. Any time a system takes
|
||||
/// mutable access to this resource this causes a conflict, but they rarely actually
|
||||
/// modify the same underlying asset.
|
||||
fn init_asset<A: Asset>(&mut self) -> &mut Self;
|
||||
/// Registers the asset type `T` using `[App::register]`,
|
||||
/// and adds [`ReflectAsset`] type data to `T` and [`ReflectHandle`] type data to [`Handle<T>`] in the type registry.
|
||||
|
@ -301,6 +305,7 @@ impl AssetApp for App {
|
|||
));
|
||||
}
|
||||
self.insert_resource(assets)
|
||||
.allow_ambiguous_resource::<Assets<A>>()
|
||||
.add_event::<AssetEvent<A>>()
|
||||
.register_type::<Handle<A>>()
|
||||
.register_type::<AssetId<A>>()
|
||||
|
@ -422,19 +427,24 @@ mod tests {
|
|||
Reader,
|
||||
},
|
||||
loader::{AssetLoader, LoadContext},
|
||||
Asset, AssetApp, AssetEvent, AssetId, AssetPlugin, AssetProvider, AssetProviders,
|
||||
AssetServer, Assets, DependencyLoadState, LoadState, RecursiveDependencyLoadState,
|
||||
Asset, AssetApp, AssetEvent, AssetId, AssetPath, AssetPlugin, AssetProvider,
|
||||
AssetProviders, AssetServer, Assets, DependencyLoadState, LoadState,
|
||||
RecursiveDependencyLoadState,
|
||||
};
|
||||
use bevy_app::{App, Update};
|
||||
use bevy_core::TaskPoolPlugin;
|
||||
use bevy_ecs::event::ManualEventReader;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::{
|
||||
event::ManualEventReader,
|
||||
schedule::{LogLevel, ScheduleBuildSettings},
|
||||
};
|
||||
use bevy_log::LogPlugin;
|
||||
use bevy_reflect::TypePath;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use futures_lite::AsyncReadExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Asset, TypePath, Debug)]
|
||||
pub struct CoolText {
|
||||
|
@ -462,24 +472,40 @@ mod tests {
|
|||
#[derive(Default)]
|
||||
struct CoolTextLoader;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum CoolTextLoaderError {
|
||||
#[error("Could not load dependency: {dependency}")]
|
||||
CannotLoadDependency { dependency: AssetPath<'static> },
|
||||
#[error("A RON error occurred during loading")]
|
||||
RonSpannedError(#[from] ron::error::SpannedError),
|
||||
#[error("An IO error occurred during loading")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl AssetLoader for CoolTextLoader {
|
||||
type Asset = CoolText;
|
||||
|
||||
type Settings = ();
|
||||
|
||||
type Error = CoolTextLoaderError;
|
||||
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
reader: &'a mut Reader,
|
||||
_settings: &'a Self::Settings,
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<Self::Asset, anyhow::Error>> {
|
||||
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
||||
Box::pin(async move {
|
||||
let mut bytes = Vec::new();
|
||||
reader.read_to_end(&mut bytes).await?;
|
||||
let mut ron: CoolTextRon = ron::de::from_bytes(&bytes)?;
|
||||
let mut embedded = String::new();
|
||||
for dep in ron.embedded_dependencies {
|
||||
let loaded = load_context.load_direct(&dep).await?;
|
||||
let loaded = load_context.load_direct(&dep).await.map_err(|_| {
|
||||
Self::Error::CannotLoadDependency {
|
||||
dependency: dep.into(),
|
||||
}
|
||||
})?;
|
||||
let cool = loaded.get::<CoolText>().unwrap();
|
||||
embedded.push_str(&cool.text);
|
||||
}
|
||||
|
@ -1166,4 +1192,23 @@ mod tests {
|
|||
None
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_system_ambiguities_on_assets() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(AssetPlugin::default())
|
||||
.init_asset::<CoolText>();
|
||||
|
||||
fn uses_assets(_asset: ResMut<Assets<CoolText>>) {}
|
||||
app.add_systems(Update, (uses_assets, uses_assets));
|
||||
app.edit_schedule(Update, |s| {
|
||||
s.set_build_settings(ScheduleBuildSettings {
|
||||
ambiguity_detection: LogLevel::Error,
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
|
||||
// running schedule does not error on ambiguity between the 2 uses_assets systems
|
||||
app.world.run_schedule(Update);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,13 +26,15 @@ pub trait AssetLoader: Send + Sync + 'static {
|
|||
type Asset: crate::Asset;
|
||||
/// The settings type used by this [`AssetLoader`].
|
||||
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
|
||||
/// The type of [error](`std::error::Error`) which could be encountered by this loader.
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
/// Asynchronously loads [`AssetLoader::Asset`] (and any other labeled assets) from the bytes provided by [`Reader`].
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
reader: &'a mut Reader,
|
||||
settings: &'a Self::Settings,
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<Self::Asset, anyhow::Error>>;
|
||||
) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>>;
|
||||
|
||||
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
|
||||
fn extensions(&self) -> &[&str];
|
||||
|
@ -46,7 +48,10 @@ pub trait ErasedAssetLoader: Send + Sync + 'static {
|
|||
reader: &'a mut Reader,
|
||||
meta: Box<dyn AssetMetaDyn>,
|
||||
load_context: LoadContext<'a>,
|
||||
) -> BoxedFuture<'a, Result<ErasedLoadedAsset, AssetLoaderError>>;
|
||||
) -> BoxedFuture<
|
||||
'a,
|
||||
Result<ErasedLoadedAsset, Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
>;
|
||||
|
||||
/// Returns a list of extensions supported by this asset loader, without the preceding dot.
|
||||
fn extensions(&self) -> &[&str];
|
||||
|
@ -64,17 +69,6 @@ pub trait ErasedAssetLoader: Send + Sync + 'static {
|
|||
fn asset_type_id(&self) -> TypeId;
|
||||
}
|
||||
|
||||
/// An error encountered during [`AssetLoader::load`].
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AssetLoaderError {
|
||||
/// Any error that occurs during load.
|
||||
#[error(transparent)]
|
||||
Load(#[from] anyhow::Error),
|
||||
/// A failure to deserialize metadata during load.
|
||||
#[error(transparent)]
|
||||
DeserializeMeta(#[from] DeserializeMetaError),
|
||||
}
|
||||
|
||||
impl<L> ErasedAssetLoader for L
|
||||
where
|
||||
L: AssetLoader + Send + Sync,
|
||||
|
@ -85,7 +79,10 @@ where
|
|||
reader: &'a mut Reader,
|
||||
meta: Box<dyn AssetMetaDyn>,
|
||||
mut load_context: LoadContext<'a>,
|
||||
) -> BoxedFuture<'a, Result<ErasedLoadedAsset, AssetLoaderError>> {
|
||||
) -> BoxedFuture<
|
||||
'a,
|
||||
Result<ErasedLoadedAsset, Box<dyn std::error::Error + Send + Sync + 'static>>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
let settings = meta
|
||||
.loader_settings()
|
||||
|
|
|
@ -193,12 +193,13 @@ impl VisitAssetDependencies for () {
|
|||
impl AssetLoader for () {
|
||||
type Asset = ();
|
||||
type Settings = ();
|
||||
type Error = std::io::Error;
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
_reader: &'a mut crate::io::Reader,
|
||||
_settings: &'a Self::Settings,
|
||||
_load_context: &'a mut crate::LoadContext,
|
||||
) -> bevy_utils::BoxedFuture<'a, Result<Self::Asset, anyhow::Error>> {
|
||||
) -> bevy_utils::BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ use crate::{
|
|||
get_asset_hash, get_full_asset_hash, AssetAction, AssetActionMinimal, AssetHash, AssetMeta,
|
||||
AssetMetaDyn, AssetMetaMinimal, ProcessedInfo, ProcessedInfoMinimal,
|
||||
},
|
||||
AssetLoadError, AssetLoaderError, AssetPath, AssetServer, DeserializeMetaError,
|
||||
LoadDirectError, MissingAssetLoaderForExtensionError, CANNOT_WATCH_ERROR_MESSAGE,
|
||||
AssetLoadError, AssetPath, AssetServer, DeserializeMetaError,
|
||||
MissingAssetLoaderForExtensionError, CANNOT_WATCH_ERROR_MESSAGE,
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_log::{debug, error, trace, warn};
|
||||
|
@ -166,7 +166,7 @@ impl AssetProcessor {
|
|||
let processor = _processor.clone();
|
||||
std::thread::spawn(move || {
|
||||
processor.process_assets();
|
||||
futures_lite::future::block_on(processor.listen_for_source_change_events());
|
||||
bevy_tasks::block_on(processor.listen_for_source_change_events());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ impl AssetProcessor {
|
|||
});
|
||||
// This must happen _after_ the scope resolves or it will happen "too early"
|
||||
// Don't move this into the async scope above! process_assets is a blocking/sync function this is fine
|
||||
futures_lite::future::block_on(self.finish_processing_assets());
|
||||
bevy_tasks::block_on(self.finish_processing_assets());
|
||||
let end_time = std::time::Instant::now();
|
||||
debug!("Processing finished in {:?}", end_time - start_time);
|
||||
}
|
||||
|
@ -817,7 +817,7 @@ impl AssetProcessor {
|
|||
break;
|
||||
}
|
||||
LogEntryError::UnfinishedTransaction(path) => {
|
||||
debug!("Asset {path:?} did not finish processing. Clearning state for that asset");
|
||||
debug!("Asset {path:?} did not finish processing. Clearing state for that asset");
|
||||
if let Err(err) = self.destination_writer().remove(&path).await {
|
||||
match err {
|
||||
AssetWriterError::Io(err) => {
|
||||
|
@ -991,7 +991,7 @@ pub(crate) struct ProcessorAssetInfo {
|
|||
/// * when processing assets in parallel, the processor might read an asset's process_dependencies when processing new versions of those dependencies
|
||||
/// * this second scenario almost certainly isn't possible with the current implementation, but its worth protecting against
|
||||
/// This lock defends against those scenarios by ensuring readers don't read while processed files are being written. And it ensures
|
||||
/// Because this lock is shared across meta and asset bytes, readers can esure they don't read "old" versions of metadata with "new" asset data.
|
||||
/// Because this lock is shared across meta and asset bytes, readers can ensure they don't read "old" versions of metadata with "new" asset data.
|
||||
pub(crate) file_transaction_lock: Arc<async_lock::RwLock<()>>,
|
||||
status_sender: async_broadcast::Sender<ProcessStatus>,
|
||||
status_receiver: async_broadcast::Receiver<ProcessStatus>,
|
||||
|
@ -1130,20 +1130,17 @@ impl ProcessorAssetInfos {
|
|||
Err(err) => {
|
||||
error!("Failed to process asset {:?}: {:?}", asset_path, err);
|
||||
// if this failed because a dependency could not be loaded, make sure it is reprocessed if that dependency is reprocessed
|
||||
if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError {
|
||||
error: AssetLoaderError::Load(loader_error),
|
||||
..
|
||||
if let ProcessError::AssetLoadError(AssetLoadError::CannotLoadDependency {
|
||||
path: dependency,
|
||||
}) = err
|
||||
{
|
||||
if let Some(error) = loader_error.downcast_ref::<LoadDirectError>() {
|
||||
let info = self.get_mut(&asset_path).expect("info should exist");
|
||||
info.processed_info = Some(ProcessedInfo {
|
||||
hash: AssetHash::default(),
|
||||
full_hash: AssetHash::default(),
|
||||
process_dependencies: vec![],
|
||||
});
|
||||
self.add_dependant(&error.dependency, asset_path.to_owned());
|
||||
}
|
||||
let info = self.get_mut(&asset_path).expect("info should exist");
|
||||
info.processed_info = Some(ProcessedInfo {
|
||||
hash: AssetHash::default(),
|
||||
full_hash: AssetHash::default(),
|
||||
process_dependencies: vec![],
|
||||
});
|
||||
self.add_dependant(&dependency, asset_path.to_owned());
|
||||
}
|
||||
|
||||
let info = self.get_mut(&asset_path).expect("info should exist");
|
||||
|
|
|
@ -91,7 +91,7 @@ pub enum ProcessError {
|
|||
#[error("The wrong meta type was passed into a processor. This is probably an internal implementation error.")]
|
||||
WrongMetaType,
|
||||
#[error("Encountered an error while saving the asset: {0}")]
|
||||
AssetSaveError(anyhow::Error),
|
||||
AssetSaveError(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
#[error("Assets without extensions are not supported.")]
|
||||
ExtensionRequired,
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ impl<Loader: AssetLoader, Saver: AssetSaver<Asset = Loader::Asset>> Process
|
|||
.saver
|
||||
.save(writer, saved_asset, &settings.saver_settings)
|
||||
.await
|
||||
.map_err(ProcessError::AssetSaveError)?;
|
||||
.map_err(|error| ProcessError::AssetSaveError(Box::new(error)))?;
|
||||
Ok(output_settings)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,9 +7,14 @@ use std::ops::Deref;
|
|||
/// Saves an [`Asset`] of a given [`AssetSaver::Asset`] type. [`AssetSaver::OutputLoader`] will then be used to load the saved asset
|
||||
/// in the final deployed application. The saver should produce asset bytes in a format that [`AssetSaver::OutputLoader`] can read.
|
||||
pub trait AssetSaver: Send + Sync + 'static {
|
||||
/// The top level [`Asset`] saved by this [`AssetSaver`].
|
||||
type Asset: Asset;
|
||||
/// The settings type used by this [`AssetSaver`].
|
||||
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
|
||||
/// The type of [`AssetLoader`] used to load this [`Asset`]
|
||||
type OutputLoader: AssetLoader;
|
||||
/// The type of [error](`std::error::Error`) which could be encountered by this saver.
|
||||
type Error: std::error::Error + Send + Sync + 'static;
|
||||
|
||||
/// Saves the given runtime [`Asset`] by writing it to a byte format using `writer`. The passed in `settings` can influence how the
|
||||
/// `asset` is saved.
|
||||
|
@ -18,7 +23,7 @@ pub trait AssetSaver: Send + Sync + 'static {
|
|||
writer: &'a mut Writer,
|
||||
asset: SavedAsset<'a, Self::Asset>,
|
||||
settings: &'a Self::Settings,
|
||||
) -> BoxedFuture<'a, Result<<Self::OutputLoader as AssetLoader>::Settings, anyhow::Error>>;
|
||||
) -> BoxedFuture<'a, Result<<Self::OutputLoader as AssetLoader>::Settings, Self::Error>>;
|
||||
}
|
||||
|
||||
/// A type-erased dynamic variant of [`AssetSaver`] that allows callers to save assets without knowing the actual type of the [`AssetSaver`].
|
||||
|
@ -30,7 +35,7 @@ pub trait ErasedAssetSaver: Send + Sync + 'static {
|
|||
writer: &'a mut Writer,
|
||||
asset: &'a ErasedLoadedAsset,
|
||||
settings: &'a dyn Settings,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>>;
|
||||
) -> BoxedFuture<'a, Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>>;
|
||||
|
||||
/// The type name of the [`AssetSaver`].
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
@ -42,7 +47,7 @@ impl<S: AssetSaver> ErasedAssetSaver for S {
|
|||
writer: &'a mut Writer,
|
||||
asset: &'a ErasedLoadedAsset,
|
||||
settings: &'a dyn Settings,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
|
||||
) -> BoxedFuture<'a, Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>> {
|
||||
Box::pin(async move {
|
||||
let settings = settings
|
||||
.downcast_ref::<S::Settings>()
|
||||
|
|
|
@ -3,7 +3,7 @@ mod info;
|
|||
use crate::{
|
||||
folder::LoadedFolder,
|
||||
io::{AssetReader, AssetReaderError, AssetSourceEvent, AssetWatcher, Reader},
|
||||
loader::{AssetLoader, AssetLoaderError, ErasedAssetLoader, LoadContext, LoadedAsset},
|
||||
loader::{AssetLoader, ErasedAssetLoader, LoadContext, LoadedAsset},
|
||||
meta::{
|
||||
loader_settings_meta_transform, AssetActionMinimal, AssetMetaDyn, AssetMetaMinimal,
|
||||
MetaTransform, Settings,
|
||||
|
@ -666,11 +666,9 @@ impl AssetServer {
|
|||
}
|
||||
};
|
||||
let loader = self.get_asset_loader_with_type_name(&loader_name).await?;
|
||||
let meta = loader.deserialize_meta(&meta_bytes).map_err(|e| {
|
||||
AssetLoadError::AssetLoaderError {
|
||||
let meta = loader.deserialize_meta(&meta_bytes).map_err(|_| {
|
||||
AssetLoadError::CannotLoadDependency {
|
||||
path: asset_path.clone().into_owned(),
|
||||
loader: loader.type_name(),
|
||||
error: AssetLoaderError::DeserializeMeta(e),
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -698,13 +696,10 @@ impl AssetServer {
|
|||
let asset_path = asset_path.clone().into_owned();
|
||||
let load_context =
|
||||
LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes);
|
||||
loader.load(reader, meta, load_context).await.map_err(|e| {
|
||||
AssetLoadError::AssetLoaderError {
|
||||
loader: loader.type_name(),
|
||||
path: asset_path,
|
||||
error: e,
|
||||
}
|
||||
})
|
||||
loader
|
||||
.load(reader, meta, load_context)
|
||||
.await
|
||||
.map_err(|_| AssetLoadError::CannotLoadDependency { path: asset_path })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -861,12 +856,8 @@ pub enum AssetLoadError {
|
|||
CannotLoadProcessedAsset { path: AssetPath<'static> },
|
||||
#[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")]
|
||||
CannotLoadIgnoredAsset { path: AssetPath<'static> },
|
||||
#[error("Asset '{path}' encountered an error in {loader}: {error}")]
|
||||
AssetLoaderError {
|
||||
path: AssetPath<'static>,
|
||||
loader: &'static str,
|
||||
error: AssetLoaderError,
|
||||
},
|
||||
#[error("Asset '{path}' is a dependency. It cannot be loaded directly.")]
|
||||
CannotLoadDependency { path: AssetPath<'static> },
|
||||
}
|
||||
|
||||
/// An error that occurs when an [`AssetLoader`] is not registered for a given extension.
|
||||
|
|
|
@ -21,7 +21,6 @@ bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
|||
|
||||
# other
|
||||
rodio = { version = "0.17", default-features = false }
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
oboe = { version = "0.5", optional = true }
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::AudioSink;
|
|||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// Initializing this resource will leak [`rodio::OutputStream`](rodio::OutputStream)
|
||||
/// Initializing this resource will leak [`rodio::OutputStream`]
|
||||
/// using [`std::mem::forget`].
|
||||
/// This is done to avoid storing this in the struct (and making this `!Send`)
|
||||
/// while preventing it from dropping (to avoid halting of audio).
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use bevy_asset::{
|
||||
anyhow::Error,
|
||||
io::{AsyncReadExt, Reader},
|
||||
Asset, AssetLoader, LoadContext,
|
||||
};
|
||||
|
@ -42,13 +41,14 @@ pub struct AudioLoader;
|
|||
impl AssetLoader for AudioLoader {
|
||||
type Asset = AudioSource;
|
||||
type Settings = ();
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
reader: &'a mut Reader,
|
||||
_settings: &'a Self::Settings,
|
||||
_load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<AudioSource, Error>> {
|
||||
) -> BoxedFuture<'a, Result<AudioSource, Self::Error>> {
|
||||
Box::pin(async move {
|
||||
let mut bytes = Vec::new();
|
||||
reader.read_to_end(&mut bytes).await?;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
fn coords_to_ray_direction(position: vec2<f32>, viewport: vec4<f32>) -> vec3<f32> {
|
||||
// Using world positions of the fragment and camera to calculate a ray direction
|
||||
// break down at large translations. This code only needs to know the ray direction.
|
||||
// breaks down at large translations. This code only needs to know the ray direction.
|
||||
// The ray direction is along the direction from the camera to the fragment position.
|
||||
// In view space, the camera is at the origin, so the view space ray direction is
|
||||
// along the direction of the fragment position - (0,0,0) which is just the
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
use proc_macro::{Span, TokenStream};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, Data::Enum, DeriveInput};
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
use crate::bevy_ecs_path;
|
||||
|
||||
pub fn derive_states(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let error = || {
|
||||
syn::Error::new(
|
||||
Span::call_site().into(),
|
||||
"derive(States) only supports fieldless enums",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into()
|
||||
};
|
||||
let Enum(enumeration) = ast.data else {
|
||||
return error();
|
||||
};
|
||||
if enumeration.variants.iter().any(|v| !v.fields.is_empty()) {
|
||||
return error();
|
||||
}
|
||||
|
||||
let generics = ast.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
|
@ -28,17 +13,9 @@ pub fn derive_states(input: TokenStream) -> TokenStream {
|
|||
trait_path.segments.push(format_ident!("schedule").into());
|
||||
trait_path.segments.push(format_ident!("States").into());
|
||||
let struct_name = &ast.ident;
|
||||
let idents = enumeration.variants.iter().map(|v| &v.ident);
|
||||
let len = idents.len();
|
||||
|
||||
quote! {
|
||||
impl #impl_generics #trait_path for #struct_name #ty_generics #where_clause {
|
||||
type Iter = std::array::IntoIter<Self, #len>;
|
||||
|
||||
fn variants() -> Self::Iter {
|
||||
[#(Self::#idents,)*].into_iter()
|
||||
}
|
||||
}
|
||||
impl #impl_generics #trait_path for #struct_name #ty_generics #where_clause {}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::{
|
|||
};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
ops::{Index, IndexMut},
|
||||
ops::{Index, IndexMut, RangeFrom},
|
||||
};
|
||||
|
||||
/// An opaque location within a [`Archetype`].
|
||||
|
@ -70,7 +70,7 @@ impl ArchetypeRow {
|
|||
///
|
||||
/// [`World`]: crate::world::World
|
||||
/// [`EMPTY`]: crate::archetype::ArchetypeId::EMPTY
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation
|
||||
#[repr(transparent)]
|
||||
pub struct ArchetypeId(u32);
|
||||
|
@ -83,13 +83,26 @@ impl ArchetypeId {
|
|||
/// This must always have an all-1s bit pattern to ensure soundness in fast entity id space allocation.
|
||||
pub const INVALID: ArchetypeId = ArchetypeId(u32::MAX);
|
||||
|
||||
/// Create an `ArchetypeId` from a plain value.
|
||||
///
|
||||
/// This is useful if you need to store the `ArchetypeId` as a plain value,
|
||||
/// for example in a specialized data structure such as a bitset.
|
||||
///
|
||||
/// While it doesn't break any safety invariants, you should ensure the
|
||||
/// values comes from a pre-existing [`ArchetypeId::index`] in this world
|
||||
/// to avoid panics and other unexpected behaviors.
|
||||
#[inline]
|
||||
pub(crate) const fn new(index: usize) -> Self {
|
||||
pub const fn new(index: usize) -> Self {
|
||||
ArchetypeId(index as u32)
|
||||
}
|
||||
|
||||
/// The plain value of this `ArchetypeId`.
|
||||
///
|
||||
/// In bevy, this is mostly used to store archetype ids in [`FixedBitSet`]s.
|
||||
///
|
||||
/// [`FixedBitSet`]: fixedbitset::FixedBitSet
|
||||
#[inline]
|
||||
pub(crate) fn index(self) -> usize {
|
||||
pub fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
@ -525,24 +538,23 @@ impl Archetype {
|
|||
}
|
||||
}
|
||||
|
||||
/// An opaque generational id that changes every time the set of [`Archetypes`] changes.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct ArchetypeGeneration(usize);
|
||||
/// The next [`ArchetypeId`] in an [`Archetypes`] collection.
|
||||
///
|
||||
/// This is used in archetype update methods to limit archetype updates to the
|
||||
/// ones added since the last time the method ran.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ArchetypeGeneration(ArchetypeId);
|
||||
|
||||
impl ArchetypeGeneration {
|
||||
/// The first archetype.
|
||||
#[inline]
|
||||
pub(crate) const fn initial() -> Self {
|
||||
ArchetypeGeneration(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn value(self) -> usize {
|
||||
self.0
|
||||
pub const fn initial() -> Self {
|
||||
ArchetypeGeneration(ArchetypeId::EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct ArchetypeIdentity {
|
||||
struct ArchetypeComponents {
|
||||
table_components: Box<[ComponentId]>,
|
||||
sparse_set_components: Box<[ComponentId]>,
|
||||
}
|
||||
|
@ -603,25 +615,29 @@ impl SparseSetIndex for ArchetypeComponentId {
|
|||
pub struct Archetypes {
|
||||
pub(crate) archetypes: Vec<Archetype>,
|
||||
pub(crate) archetype_component_count: usize,
|
||||
archetype_ids: bevy_utils::HashMap<ArchetypeIdentity, ArchetypeId>,
|
||||
by_components: bevy_utils::HashMap<ArchetypeComponents, ArchetypeId>,
|
||||
}
|
||||
|
||||
impl Archetypes {
|
||||
pub(crate) fn new() -> Self {
|
||||
let mut archetypes = Archetypes {
|
||||
archetypes: Vec::new(),
|
||||
archetype_ids: Default::default(),
|
||||
by_components: Default::default(),
|
||||
archetype_component_count: 0,
|
||||
};
|
||||
archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new());
|
||||
archetypes
|
||||
}
|
||||
|
||||
/// Returns the current archetype generation. This is an ID indicating the current set of archetypes
|
||||
/// that are registered with the world.
|
||||
/// Returns the "generation", a handle to the current highest archetype ID.
|
||||
///
|
||||
/// This can be used with the `Index` [`Archetypes`] implementation to
|
||||
/// iterate over newly introduced [`Archetype`]s since the last time this
|
||||
/// function was called.
|
||||
#[inline]
|
||||
pub fn generation(&self) -> ArchetypeGeneration {
|
||||
ArchetypeGeneration(self.archetypes.len())
|
||||
let id = ArchetypeId::new(self.archetypes.len());
|
||||
ArchetypeGeneration(id)
|
||||
}
|
||||
|
||||
/// Fetches the total number of [`Archetype`]s within the world.
|
||||
|
@ -692,7 +708,7 @@ impl Archetypes {
|
|||
table_components: Vec<ComponentId>,
|
||||
sparse_set_components: Vec<ComponentId>,
|
||||
) -> ArchetypeId {
|
||||
let archetype_identity = ArchetypeIdentity {
|
||||
let archetype_identity = ArchetypeComponents {
|
||||
sparse_set_components: sparse_set_components.clone().into_boxed_slice(),
|
||||
table_components: table_components.clone().into_boxed_slice(),
|
||||
};
|
||||
|
@ -700,7 +716,7 @@ impl Archetypes {
|
|||
let archetypes = &mut self.archetypes;
|
||||
let archetype_component_count = &mut self.archetype_component_count;
|
||||
*self
|
||||
.archetype_ids
|
||||
.by_components
|
||||
.entry(archetype_identity)
|
||||
.or_insert_with(move || {
|
||||
let id = ArchetypeId::new(archetypes.len());
|
||||
|
@ -739,6 +755,14 @@ impl Archetypes {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<RangeFrom<ArchetypeGeneration>> for Archetypes {
|
||||
type Output = [Archetype];
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: RangeFrom<ArchetypeGeneration>) -> &Self::Output {
|
||||
&self.archetypes[index.start.0.index()..]
|
||||
}
|
||||
}
|
||||
impl Index<ArchetypeId> for Archetypes {
|
||||
type Output = Archetype;
|
||||
|
||||
|
|
|
@ -381,6 +381,15 @@ macro_rules! impl_methods {
|
|||
ticks: self.ticks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows you access to the dereferenced value of this pointer without immediately
|
||||
/// triggering change detection.
|
||||
pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>
|
||||
where $target: DerefMut
|
||||
{
|
||||
self.reborrow().map_unchanged(|v| v.deref_mut())
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -526,7 +535,7 @@ impl_debug!(Res<'w, T>, Resource);
|
|||
///
|
||||
/// See the [`Resource`] documentation for usage.
|
||||
///
|
||||
/// If you need a shared borrow, use [`Res`](crate::system::Res) instead.
|
||||
/// If you need a shared borrow, use [`Res`] instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -907,6 +916,7 @@ mod tests {
|
|||
use bevy_ecs_macros::Resource;
|
||||
use bevy_ptr::PtrMut;
|
||||
use bevy_reflect::{FromType, ReflectFromPtr};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
self as bevy_ecs,
|
||||
|
@ -929,6 +939,19 @@ mod tests {
|
|||
#[derive(Resource, PartialEq)]
|
||||
struct R2(u8);
|
||||
|
||||
impl Deref for R2 {
|
||||
type Target = u8;
|
||||
fn deref(&self) -> &u8 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for R2 {
|
||||
fn deref_mut(&mut self) -> &mut u8 {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_expiration() {
|
||||
fn change_detected(query: Query<Ref<C>>) -> bool {
|
||||
|
@ -1143,6 +1166,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_deref_mut() {
|
||||
let mut world = World::new();
|
||||
|
||||
world.insert_resource(R2(0));
|
||||
// Resources are Changed when first added
|
||||
world.increment_change_tick();
|
||||
// This is required to update world::last_change_tick
|
||||
world.clear_trackers();
|
||||
|
||||
let mut r = world.resource_mut::<R2>();
|
||||
assert!(!r.is_changed(), "Resource must begin unchanged.");
|
||||
|
||||
let mut r = r.as_deref_mut();
|
||||
assert!(
|
||||
!r.is_changed(),
|
||||
"Dereferencing should not mark the item as changed yet"
|
||||
);
|
||||
|
||||
r.set_if_neq(3);
|
||||
assert!(
|
||||
r.is_changed(),
|
||||
"Resource must be changed after setting to a different value."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mut_untyped_to_reflect() {
|
||||
let last_run = Tick::new(2);
|
||||
|
|
|
@ -266,7 +266,7 @@ impl ComponentInfo {
|
|||
}
|
||||
|
||||
/// A value which uniquely identifies the type of a [`Component`] within a
|
||||
/// [`World`](crate::world::World).
|
||||
/// [`World`].
|
||||
///
|
||||
/// Each time a new `Component` type is registered within a `World` using
|
||||
/// [`World::init_component`](crate::world::World::init_component) or
|
||||
|
|
|
@ -44,7 +44,7 @@ use crate::{
|
|||
storage::{SparseSetIndex, TableId, TableRow},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryFrom, fmt, mem, sync::atomic::Ordering};
|
||||
use std::{convert::TryFrom, fmt, hash::Hash, mem, sync::atomic::Ordering};
|
||||
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
use std::sync::atomic::AtomicI64 as AtomicIdCursor;
|
||||
|
@ -115,12 +115,19 @@ type IdCursor = isize;
|
|||
/// [`EntityCommands`]: crate::system::EntityCommands
|
||||
/// [`Query::get`]: crate::system::Query::get
|
||||
/// [`World`]: crate::world::World
|
||||
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Entity {
|
||||
generation: u32,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
impl Hash for Entity {
|
||||
#[inline]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum AllocAtWithoutReplacement {
|
||||
Exists(EntityLocation),
|
||||
DidNotExist,
|
||||
|
|
|
@ -427,8 +427,7 @@ macro_rules! impl_tick_filter {
|
|||
table_ticks: None,
|
||||
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
|
||||
.then(|| {
|
||||
world.unsafe_world()
|
||||
.storages()
|
||||
world.storages()
|
||||
.sparse_sets
|
||||
.get(id)
|
||||
.debug_checked_unwrap()
|
||||
|
@ -617,10 +616,10 @@ impl_tick_filter!(
|
|||
|
||||
/// A marker trait to indicate that the filter works at an archetype level.
|
||||
///
|
||||
/// This is needed to implement [`ExactSizeIterator`](std::iter::ExactSizeIterator) for
|
||||
/// This is needed to implement [`ExactSizeIterator`] for
|
||||
/// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters.
|
||||
///
|
||||
/// The trait must only be implement for filters where its corresponding [`WorldQuery::IS_ARCHETYPAL`](crate::query::WorldQuery::IS_ARCHETYPAL)
|
||||
/// The trait must only be implement for filters where its corresponding [`WorldQuery::IS_ARCHETYPAL`]
|
||||
/// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait.
|
||||
/// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types
|
||||
/// also implement the same trait.
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
|
||||
};
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::Instrument;
|
||||
use bevy_utils::tracing::Span;
|
||||
use fixedbitset::FixedBitSet;
|
||||
use std::{any::TypeId, borrow::Borrow, fmt, mem::MaybeUninit};
|
||||
|
||||
|
@ -39,6 +39,8 @@ pub struct QueryState<Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
|
|||
pub(crate) matched_archetype_ids: Vec<ArchetypeId>,
|
||||
pub(crate) fetch_state: Q::State,
|
||||
pub(crate) filter_state: F::State,
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: Span,
|
||||
}
|
||||
|
||||
impl<Q: WorldQuery, F: ReadOnlyWorldQuery> std::fmt::Debug for QueryState<Q, F> {
|
||||
|
@ -125,6 +127,12 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
matched_tables: Default::default(),
|
||||
matched_archetypes: Default::default(),
|
||||
archetype_component_access: Default::default(),
|
||||
#[cfg(feature = "trace")]
|
||||
par_iter_span: bevy_utils::tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = std::any::type_name::<Q>(),
|
||||
filter = std::any::type_name::<F>(),
|
||||
),
|
||||
};
|
||||
state.update_archetypes(world);
|
||||
state
|
||||
|
@ -208,12 +216,11 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
|
||||
self.validate_world(world.id());
|
||||
let archetypes = world.archetypes();
|
||||
let new_generation = archetypes.generation();
|
||||
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
|
||||
let archetype_index_range = old_generation.value()..new_generation.value();
|
||||
let old_generation =
|
||||
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
||||
|
||||
for archetype_index in archetype_index_range {
|
||||
self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]);
|
||||
for archetype in &archetypes[old_generation..] {
|
||||
self.new_archetype(archetype);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,10 +337,12 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
|
||||
self.update_archetypes(world);
|
||||
|
||||
// SAFETY: update_archetypes validates the `World` matches
|
||||
// SAFETY:
|
||||
// - We have read-only access to the entire world.
|
||||
// - `update_archetypes` validates that the `World` matches.
|
||||
unsafe {
|
||||
self.get_many_read_only_manual(
|
||||
world,
|
||||
world.as_unsafe_world_cell_readonly(),
|
||||
entities,
|
||||
world.last_change_tick(),
|
||||
world.read_change_tick(),
|
||||
|
@ -625,11 +634,13 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This must be called on the same `World` that the `Query` was generated from:
|
||||
/// * `world` must have permission to read all of the components returned from this call.
|
||||
/// No mutable references may coexist with any of the returned references.
|
||||
/// * This must be called on the same `World` that the `Query` was generated from:
|
||||
/// use `QueryState::validate_world` to verify this.
|
||||
pub(crate) unsafe fn get_many_read_only_manual<'w, const N: usize>(
|
||||
&self,
|
||||
world: &'w World,
|
||||
world: UnsafeWorldCell<'w>,
|
||||
entities: [Entity; N],
|
||||
last_run: Tick,
|
||||
this_run: Tick,
|
||||
|
@ -639,12 +650,9 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
for (value, entity) in std::iter::zip(&mut values, entities) {
|
||||
// SAFETY: fetch is read-only
|
||||
// and world must be validated
|
||||
let item = self.as_readonly().get_unchecked_manual(
|
||||
world.as_unsafe_world_cell_readonly(),
|
||||
entity,
|
||||
last_run,
|
||||
this_run,
|
||||
)?;
|
||||
let item = self
|
||||
.as_readonly()
|
||||
.get_unchecked_manual(world, entity, last_run, this_run)?;
|
||||
*value = MaybeUninit::new(item);
|
||||
}
|
||||
|
||||
|
@ -1205,7 +1213,9 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
while offset < table.entity_count() {
|
||||
let func = func.clone();
|
||||
let len = batch_size.min(table.entity_count() - offset);
|
||||
let task = async move {
|
||||
scope.spawn(async move {
|
||||
#[cfg(feature = "trace")]
|
||||
let _span = self.par_iter_span.enter();
|
||||
let mut fetch =
|
||||
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
|
||||
let mut filter =
|
||||
|
@ -1223,17 +1233,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
}
|
||||
func(Q::fetch(&mut fetch, *entity, row));
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "trace")]
|
||||
let span = bevy_utils::tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = std::any::type_name::<Q>(),
|
||||
filter = std::any::type_name::<F>(),
|
||||
count = len,
|
||||
);
|
||||
#[cfg(feature = "trace")]
|
||||
let task = task.instrument(span);
|
||||
scope.spawn(task);
|
||||
});
|
||||
offset += batch_size;
|
||||
}
|
||||
}
|
||||
|
@ -1249,7 +1249,9 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
while offset < archetype.len() {
|
||||
let func = func.clone();
|
||||
let len = batch_size.min(archetype.len() - offset);
|
||||
let task = async move {
|
||||
scope.spawn(async move {
|
||||
#[cfg(feature = "trace")]
|
||||
let _span = self.par_iter_span.enter();
|
||||
let mut fetch =
|
||||
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
|
||||
let mut filter =
|
||||
|
@ -1277,19 +1279,8 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
archetype_entity.table_row(),
|
||||
));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
let span = bevy_utils::tracing::info_span!(
|
||||
"par_for_each",
|
||||
query = std::any::type_name::<Q>(),
|
||||
filter = std::any::type_name::<F>(),
|
||||
count = len,
|
||||
);
|
||||
#[cfg(feature = "trace")]
|
||||
let task = task.instrument(span);
|
||||
|
||||
scope.spawn(task);
|
||||
offset += batch_size;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,7 +279,7 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
|
|||
},
|
||||
reflect_unchecked_mut: |entity| {
|
||||
// SAFETY: reflect_unchecked_mut is an unsafe function pointer used by
|
||||
// `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the the component `C` on the `entity`
|
||||
// `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the component `C` on the `entity`
|
||||
unsafe {
|
||||
entity.get_mut::<C>().map(|c| Mut {
|
||||
value: c.value as &mut dyn Reflect,
|
||||
|
|
|
@ -259,8 +259,7 @@ impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> {
|
|||
// SAFETY: Only reads World removed component events
|
||||
unsafe impl<'a> ReadOnlySystemParam for &'a RemovedComponentEvents {}
|
||||
|
||||
// SAFETY: no component value access, removed component events can be read in parallel and are
|
||||
// never mutably borrowed during system execution
|
||||
// SAFETY: no component value access.
|
||||
unsafe impl<'a> SystemParam for &'a RemovedComponentEvents {
|
||||
type State = ();
|
||||
type Item<'w, 's> = &'w RemovedComponentEvents;
|
||||
|
@ -274,6 +273,6 @@ unsafe impl<'a> SystemParam for &'a RemovedComponentEvents {
|
|||
world: UnsafeWorldCell<'w>,
|
||||
_change_tick: Tick,
|
||||
) -> Self::Item<'w, 's> {
|
||||
world.world_metadata().removed_components()
|
||||
world.removed_components()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1096,7 +1096,7 @@ mod tests {
|
|||
schedule.run(&mut world);
|
||||
assert_eq!(world.resource::<Counter>().0, 2);
|
||||
|
||||
// Run every other cycle oppsite to the last one
|
||||
// Run every other cycle opposite to the last one
|
||||
schedule.add_systems(increment_counter.run_if(not(every_other_time)));
|
||||
|
||||
schedule.run(&mut world);
|
||||
|
@ -1190,7 +1190,7 @@ mod tests {
|
|||
.distributive_run_if(resource_changed_or_removed::<State<TestState>>())
|
||||
.distributive_run_if(resource_removed::<State<TestState>>())
|
||||
.distributive_run_if(state_exists::<TestState>())
|
||||
.distributive_run_if(in_state(TestState::A))
|
||||
.distributive_run_if(in_state(TestState::A).or_else(in_state(TestState::B)))
|
||||
.distributive_run_if(state_changed::<TestState>())
|
||||
.distributive_run_if(on_event::<TestEvent>())
|
||||
.distributive_run_if(any_with_component::<TestComponent>())
|
||||
|
|
|
@ -59,7 +59,7 @@ pub type SystemConfig = NodeConfig<BoxedSystem>;
|
|||
|
||||
/// A collections of generic [`NodeConfig`]s.
|
||||
pub enum NodeConfigs<T> {
|
||||
/// Configuratin for a single node.
|
||||
/// Configuration for a single node.
|
||||
NodeConfig(NodeConfig<T>),
|
||||
/// Configuration for a tuple of nested `Configs` instances.
|
||||
Configs {
|
||||
|
|
|
@ -718,6 +718,9 @@ mod tests {
|
|||
}
|
||||
|
||||
mod system_ambiguity {
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use super::*;
|
||||
// Required to make the derive macro behave
|
||||
use crate as bevy_ecs;
|
||||
use crate::event::Events;
|
||||
|
@ -981,14 +984,12 @@ mod tests {
|
|||
assert_eq!(schedule.graph().conflicting_systems().len(), 0);
|
||||
}
|
||||
|
||||
#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
|
||||
struct TestSchedule;
|
||||
|
||||
// Tests that the correct ambiguities were reported in the correct order.
|
||||
#[test]
|
||||
fn correct_ambiguities() {
|
||||
use super::*;
|
||||
|
||||
#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
|
||||
struct TestSchedule;
|
||||
|
||||
fn system_a(_res: ResMut<R>) {}
|
||||
fn system_b(_res: ResMut<R>) {}
|
||||
fn system_c(_res: ResMut<R>) {}
|
||||
|
@ -1008,9 +1009,11 @@ mod tests {
|
|||
));
|
||||
|
||||
schedule.graph_mut().initialize(&mut world);
|
||||
let _ = schedule
|
||||
.graph_mut()
|
||||
.build_schedule(world.components(), &TestSchedule.dyn_clone());
|
||||
let _ = schedule.graph_mut().build_schedule(
|
||||
world.components(),
|
||||
&TestSchedule.dyn_clone(),
|
||||
&BTreeSet::new(),
|
||||
);
|
||||
|
||||
let ambiguities: Vec<_> = schedule
|
||||
.graph()
|
||||
|
@ -1050,19 +1053,16 @@ mod tests {
|
|||
// Related issue https://github.com/bevyengine/bevy/issues/9641
|
||||
#[test]
|
||||
fn anonymous_set_name() {
|
||||
use super::*;
|
||||
|
||||
#[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
|
||||
struct TestSchedule;
|
||||
|
||||
let mut schedule = Schedule::new(TestSchedule);
|
||||
schedule.add_systems((resmut_system, resmut_system).run_if(|| true));
|
||||
|
||||
let mut world = World::new();
|
||||
schedule.graph_mut().initialize(&mut world);
|
||||
let _ = schedule
|
||||
.graph_mut()
|
||||
.build_schedule(world.components(), &TestSchedule.dyn_clone());
|
||||
let _ = schedule.graph_mut().build_schedule(
|
||||
world.components(),
|
||||
&TestSchedule.dyn_clone(),
|
||||
&BTreeSet::new(),
|
||||
);
|
||||
|
||||
let ambiguities: Vec<_> = schedule
|
||||
.graph()
|
||||
|
@ -1078,5 +1078,24 @@ mod tests {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignore_component_resource_ambiguities() {
|
||||
let mut world = World::new();
|
||||
world.insert_resource(R);
|
||||
world.allow_ambiguous_resource::<R>();
|
||||
let mut schedule = Schedule::new(TestSchedule);
|
||||
|
||||
//check resource
|
||||
schedule.add_systems((resmut_system, res_system));
|
||||
schedule.initialize(&mut world).unwrap();
|
||||
assert!(schedule.graph().conflicting_systems().is_empty());
|
||||
|
||||
// check components
|
||||
world.allow_ambiguous_component::<A>();
|
||||
schedule.add_systems((write_component_system, read_component_system));
|
||||
schedule.initialize(&mut world).unwrap();
|
||||
assert!(schedule.graph().conflicting_systems().is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::{
|
||||
collections::BTreeSet,
|
||||
fmt::{Debug, Write},
|
||||
result::Result,
|
||||
};
|
||||
|
||||
use bevy_utils::default;
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
use bevy_utils::{default, tracing::info};
|
||||
use bevy_utils::{
|
||||
petgraph::{algo::TarjanScc, prelude::*},
|
||||
thiserror::Error,
|
||||
|
@ -18,6 +19,7 @@ use fixedbitset::FixedBitSet;
|
|||
use crate::{
|
||||
self as bevy_ecs,
|
||||
component::{ComponentId, Components, Tick},
|
||||
prelude::Component,
|
||||
schedule::*,
|
||||
system::{BoxedSystem, Resource, System},
|
||||
world::World,
|
||||
|
@ -27,6 +29,8 @@ use crate::{
|
|||
#[derive(Default, Resource)]
|
||||
pub struct Schedules {
|
||||
inner: HashMap<BoxedScheduleLabel, Schedule>,
|
||||
/// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts
|
||||
pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
|
||||
}
|
||||
|
||||
impl Schedules {
|
||||
|
@ -34,6 +38,7 @@ impl Schedules {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: HashMap::new(),
|
||||
ignored_scheduling_ambiguities: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,6 +115,38 @@ impl Schedules {
|
|||
schedule.set_build_settings(schedule_build_settings.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`.
|
||||
pub fn allow_ambiguous_component<T: Component>(&mut self, world: &mut World) {
|
||||
self.ignored_scheduling_ambiguities
|
||||
.insert(world.init_component::<T>());
|
||||
}
|
||||
|
||||
/// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`.
|
||||
pub fn allow_ambiguous_resource<T: Resource>(&mut self, world: &mut World) {
|
||||
self.ignored_scheduling_ambiguities
|
||||
.insert(world.components.init_resource::<T>());
|
||||
}
|
||||
|
||||
/// Iterate through the [`ComponentId`]'s that will be ignored.
|
||||
pub fn iter_ignored_ambiguities(&self) -> impl Iterator<Item = &ComponentId> + '_ {
|
||||
self.ignored_scheduling_ambiguities.iter()
|
||||
}
|
||||
|
||||
/// Prints the names of the components and resources with [`info`]
|
||||
///
|
||||
/// May panic or retrieve incorrect names if [`Components`] is not from the same
|
||||
/// world
|
||||
pub fn print_ignored_ambiguities(&self, components: &Components) {
|
||||
let mut message =
|
||||
"System order ambiguities caused by conflicts on the following types are ignored:\n"
|
||||
.to_string();
|
||||
for id in self.iter_ignored_ambiguities() {
|
||||
writeln!(message, "{}", components.get_name(*id).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
info!("{}", message);
|
||||
}
|
||||
}
|
||||
|
||||
fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
|
||||
|
@ -237,7 +274,7 @@ impl Schedule {
|
|||
|
||||
/// Set whether the schedule applies deferred system buffers on final time or not. This is a catch-all
|
||||
/// in case a system uses commands but was not explicitly ordered before an instance of
|
||||
/// [`apply_deferred`](crate::prelude::apply_deferred). By default this
|
||||
/// [`apply_deferred`]. By default this
|
||||
/// setting is true, but may be disabled if needed.
|
||||
pub fn set_apply_final_deferred(&mut self, apply_final_deferred: bool) -> &mut Self {
|
||||
self.executor.set_apply_final_deferred(apply_final_deferred);
|
||||
|
@ -251,7 +288,7 @@ impl Schedule {
|
|||
|
||||
world.check_change_ticks();
|
||||
self.initialize(world)
|
||||
.unwrap_or_else(|e| panic!("Error when intializing schedule {:?}: {e}", self.name));
|
||||
.unwrap_or_else(|e| panic!("Error when initializing schedule {:?}: {e}", self.name));
|
||||
self.executor.run(&mut self.executable, world);
|
||||
}
|
||||
|
||||
|
@ -262,8 +299,16 @@ impl Schedule {
|
|||
pub fn initialize(&mut self, world: &mut World) -> Result<(), ScheduleBuildError> {
|
||||
if self.graph.changed {
|
||||
self.graph.initialize(world);
|
||||
self.graph
|
||||
.update_schedule(&mut self.executable, world.components(), &self.name)?;
|
||||
let ignored_ambiguities = world
|
||||
.get_resource_or_insert_with::<Schedules>(Schedules::default)
|
||||
.ignored_scheduling_ambiguities
|
||||
.clone();
|
||||
self.graph.update_schedule(
|
||||
&mut self.executable,
|
||||
world.components(),
|
||||
&ignored_ambiguities,
|
||||
&self.name,
|
||||
)?;
|
||||
self.graph.changed = false;
|
||||
self.executor_initialized = false;
|
||||
}
|
||||
|
@ -871,6 +916,7 @@ impl ScheduleGraph {
|
|||
&mut self,
|
||||
components: &Components,
|
||||
schedule_label: &BoxedScheduleLabel,
|
||||
ignored_ambiguities: &BTreeSet<ComponentId>,
|
||||
) -> Result<SystemSchedule, ScheduleBuildError> {
|
||||
// check hierarchy for cycles
|
||||
self.hierarchy.topsort =
|
||||
|
@ -919,8 +965,11 @@ impl ScheduleGraph {
|
|||
let ambiguous_with_flattened = self.get_ambiguous_with_flattened(&set_systems);
|
||||
|
||||
// check for conflicts
|
||||
let conflicting_systems =
|
||||
self.get_conflicting_systems(&flat_results.disconnected, &ambiguous_with_flattened);
|
||||
let conflicting_systems = self.get_conflicting_systems(
|
||||
&flat_results.disconnected,
|
||||
&ambiguous_with_flattened,
|
||||
ignored_ambiguities,
|
||||
);
|
||||
self.optionally_check_conflicts(&conflicting_systems, components, schedule_label)?;
|
||||
self.conflicting_systems = conflicting_systems;
|
||||
|
||||
|
@ -1040,6 +1089,7 @@ impl ScheduleGraph {
|
|||
&self,
|
||||
flat_results_disconnected: &Vec<(NodeId, NodeId)>,
|
||||
ambiguous_with_flattened: &GraphMap<NodeId, (), Undirected>,
|
||||
ignored_ambiguities: &BTreeSet<ComponentId>,
|
||||
) -> Vec<(NodeId, NodeId, Vec<ComponentId>)> {
|
||||
let mut conflicting_systems = Vec::new();
|
||||
for &(a, b) in flat_results_disconnected {
|
||||
|
@ -1058,8 +1108,15 @@ impl ScheduleGraph {
|
|||
let access_a = system_a.component_access();
|
||||
let access_b = system_b.component_access();
|
||||
if !access_a.is_compatible(access_b) {
|
||||
let conflicts = access_a.get_conflicts(access_b);
|
||||
conflicting_systems.push((a, b, conflicts));
|
||||
let conflicts: Vec<_> = access_a
|
||||
.get_conflicts(access_b)
|
||||
.into_iter()
|
||||
.filter(|id| !ignored_ambiguities.contains(id))
|
||||
.collect();
|
||||
|
||||
if !conflicts.is_empty() {
|
||||
conflicting_systems.push((a, b, conflicts));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1171,6 +1228,7 @@ impl ScheduleGraph {
|
|||
&mut self,
|
||||
schedule: &mut SystemSchedule,
|
||||
components: &Components,
|
||||
ignored_ambiguities: &BTreeSet<ComponentId>,
|
||||
schedule_label: &BoxedScheduleLabel,
|
||||
) -> Result<(), ScheduleBuildError> {
|
||||
if !self.uninit.is_empty() {
|
||||
|
@ -1196,7 +1254,7 @@ impl ScheduleGraph {
|
|||
self.system_set_conditions[id.index()] = conditions;
|
||||
}
|
||||
|
||||
*schedule = self.build_schedule(components, schedule_label)?;
|
||||
*schedule = self.build_schedule(components, schedule_label, ignored_ambiguities)?;
|
||||
|
||||
// move systems into new schedule
|
||||
for &id in &schedule.system_ids {
|
||||
|
@ -1570,7 +1628,7 @@ impl ScheduleGraph {
|
|||
message
|
||||
}
|
||||
|
||||
/// convert conflics to human readable format
|
||||
/// convert conflicts to human readable format
|
||||
pub fn conflicts_to_string<'a>(
|
||||
&'a self,
|
||||
ambiguities: &'a [(NodeId, NodeId, Vec<ComponentId>)],
|
||||
|
|
|
@ -40,13 +40,7 @@ pub use bevy_ecs_macros::States;
|
|||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default {
|
||||
/// The type returned when iterating over all [`variants`](States::variants) of this type.
|
||||
type Iter: Iterator<Item = Self>;
|
||||
|
||||
/// Returns an iterator over all the state variants.
|
||||
fn variants() -> Self::Iter;
|
||||
}
|
||||
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug + Default {}
|
||||
|
||||
/// The label of a [`Schedule`](super::Schedule) that runs whenever [`State<S>`]
|
||||
/// enters this state.
|
||||
|
|
|
@ -88,9 +88,9 @@ pub trait Command: Send + 'static {
|
|||
///
|
||||
/// # Implementing
|
||||
///
|
||||
/// Each built-in command is implemented as a separate method, e.g. [`spawn`](#method.spawn).
|
||||
/// Each built-in command is implemented as a separate method, e.g. [`Commands::spawn`].
|
||||
/// In addition to the pre-defined command methods, you can add commands with any arbitrary
|
||||
/// behavior using [`Commands::add`](#method.add), which accepts any type implementing [`Command`].
|
||||
/// behavior using [`Commands::add`], which accepts any type implementing [`Command`].
|
||||
///
|
||||
/// Since closures and other functions implement this trait automatically, this allows one-shot,
|
||||
/// anonymous custom commands.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
archetype::{ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
|
||||
archetype::{ArchetypeComponentId, ArchetypeGeneration},
|
||||
component::{ComponentId, Tick},
|
||||
prelude::FromWorld,
|
||||
query::{Access, FilteredAccessSet},
|
||||
|
@ -270,16 +270,11 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||
#[inline]
|
||||
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
|
||||
let archetypes = world.archetypes();
|
||||
let new_generation = archetypes.generation();
|
||||
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
|
||||
let archetype_index_range = old_generation.value()..new_generation.value();
|
||||
let old_generation =
|
||||
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
||||
|
||||
for archetype_index in archetype_index_range {
|
||||
Param::new_archetype(
|
||||
&mut self.param_state,
|
||||
&archetypes[ArchetypeId::new(archetype_index)],
|
||||
&mut self.meta,
|
||||
);
|
||||
for archetype in &archetypes[old_generation..] {
|
||||
Param::new_archetype(&mut self.param_state, archetype, &mut self.meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -515,17 +510,12 @@ where
|
|||
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
|
||||
assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
|
||||
let archetypes = world.archetypes();
|
||||
let new_generation = archetypes.generation();
|
||||
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
|
||||
let archetype_index_range = old_generation.value()..new_generation.value();
|
||||
let old_generation =
|
||||
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
||||
|
||||
for archetype_index in archetype_index_range {
|
||||
for archetype in &archetypes[old_generation..] {
|
||||
let param_state = self.param_state.as_mut().unwrap();
|
||||
F::Param::new_archetype(
|
||||
param_state,
|
||||
&archetypes[ArchetypeId::new(archetype_index)],
|
||||
&mut self.system_meta,
|
||||
);
|
||||
F::Param::new_archetype(param_state, archetype, &mut self.system_meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//!
|
||||
//! System functions can have parameters, through which one can query and mutate Bevy ECS state.
|
||||
//! Only types that implement [`SystemParam`] can be used, automatically fetching data from
|
||||
//! the [`World`](crate::world::World).
|
||||
//! the [`World`].
|
||||
//!
|
||||
//! System functions often look like this:
|
||||
//!
|
||||
|
|
|
@ -874,14 +874,12 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
&self,
|
||||
entities: [Entity; N],
|
||||
) -> Result<[ROQueryItem<'_, Q>; N], QueryEntityError> {
|
||||
// SAFETY: it is the scheduler's responsibility to ensure that `Query` is never handed out on the wrong `World`.
|
||||
// SAFETY:
|
||||
// - `&self` ensures there is no mutable access to any components accessible to this query.
|
||||
// - `self.world` matches `self.state`.
|
||||
unsafe {
|
||||
self.state.get_many_read_only_manual(
|
||||
self.world.unsafe_world(),
|
||||
entities,
|
||||
self.last_run,
|
||||
self.this_run,
|
||||
)
|
||||
self.state
|
||||
.get_many_read_only_manual(self.world, entities, self.last_run, self.this_run)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1105,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
|
||||
/// Returns a mutable reference to the component `T` of the given entity.
|
||||
///
|
||||
/// In case of a nonexisting entity, mismatched component or missing write acess, a [`QueryComponentError`] is returned instead.
|
||||
/// In case of a nonexisting entity, mismatched component or missing write access, a [`QueryComponentError`] is returned instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
|
|
@ -43,7 +43,7 @@ pub struct SystemId(Entity);
|
|||
impl World {
|
||||
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
|
||||
///
|
||||
/// It's possible to register the same systems more than once, they'll be stored seperately.
|
||||
/// It's possible to register the same systems more than once, they'll be stored separately.
|
||||
///
|
||||
/// This is different from adding systems to a [`Schedule`](crate::schedule::Schedule),
|
||||
/// because the [`SystemId`] that is returned can be used anywhere in the [`World`] to run the associated system.
|
||||
|
|
|
@ -174,7 +174,7 @@ impl<'a> From<&'a EntityWorldMut<'_>> for EntityRef<'a> {
|
|||
impl<'w> From<EntityMut<'w>> for EntityRef<'w> {
|
||||
fn from(value: EntityMut<'w>) -> Self {
|
||||
// SAFETY:
|
||||
// - `EntityMut` gurantees exclusive access to all of the entity's components.
|
||||
// - `EntityMut` guarantees exclusive access to all of the entity's components.
|
||||
unsafe { EntityRef::new(value.0) }
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ impl<'w> From<EntityMut<'w>> for EntityRef<'w> {
|
|||
impl<'a> From<&'a EntityMut<'_>> for EntityRef<'a> {
|
||||
fn from(value: &'a EntityMut<'_>) -> Self {
|
||||
// SAFETY:
|
||||
// - `EntityMut` gurantees exclusive access to all of the entity's components.
|
||||
// - `EntityMut` guarantees exclusive access to all of the entity's components.
|
||||
// - `&value` ensures there are no mutable accesses.
|
||||
unsafe { EntityRef::new(value.0) }
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@ impl World {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the components of an [`Entity`](crate::entity::Entity) through [`ComponentInfo`](crate::component::ComponentInfo).
|
||||
/// Returns the components of an [`Entity`] through [`ComponentInfo`].
|
||||
#[inline]
|
||||
pub fn inspect_entity(&self, entity: Entity) -> Vec<&ComponentInfo> {
|
||||
let entity_location = self
|
||||
|
@ -1766,7 +1766,7 @@ impl World {
|
|||
/// Iterates all component change ticks and clamps any older than [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
|
||||
/// This prevents overflow and thus prevents false positives.
|
||||
///
|
||||
/// **Note:** Does nothing if the [`World`] counter has not been incremented at least [`CHECK_TICK_THRESHOLD`](crate::change_detection::CHECK_TICK_THRESHOLD)
|
||||
/// **Note:** Does nothing if the [`World`] counter has not been incremented at least [`CHECK_TICK_THRESHOLD`]
|
||||
/// times since the previous pass.
|
||||
// TODO: benchmark and optimize
|
||||
pub fn check_change_ticks(&mut self) {
|
||||
|
@ -2087,6 +2087,20 @@ impl World {
|
|||
pub fn run_schedule(&mut self, label: impl AsRef<dyn ScheduleLabel>) {
|
||||
self.schedule_scope(label, |world, sched| sched.run(world));
|
||||
}
|
||||
|
||||
/// Ignore system order ambiguities caused by conflicts on [`Component`]s of type `T`.
|
||||
pub fn allow_ambiguous_component<T: Component>(&mut self) {
|
||||
let mut schedules = self.remove_resource::<Schedules>().unwrap_or_default();
|
||||
schedules.allow_ambiguous_component::<T>(self);
|
||||
self.insert_resource(schedules);
|
||||
}
|
||||
|
||||
/// Ignore system order ambiguities caused by conflicts on [`Resource`]s of type `T`.
|
||||
pub fn allow_ambiguous_resource<T: Resource>(&mut self) {
|
||||
let mut schedules = self.remove_resource::<Schedules>().unwrap_or_default();
|
||||
schedules.allow_ambiguous_resource::<T>(self);
|
||||
self.insert_resource(schedules);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for World {
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::{
|
|||
},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
prelude::Component,
|
||||
removal_detection::RemovedComponentEvents,
|
||||
storage::{Column, ComponentSparseSet, Storages},
|
||||
system::Resource,
|
||||
};
|
||||
|
@ -25,14 +26,14 @@ use std::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData};
|
|||
/// In rust, having a `&mut World` means that there are absolutely no other references to the safe world alive at the same time,
|
||||
/// without exceptions. Not even unsafe code can change this.
|
||||
///
|
||||
/// But there are situations where careful shared mutable access through a type is possible and safe. For this, rust provides the [`UnsafeCell`](std::cell::UnsafeCell)
|
||||
/// But there are situations where careful shared mutable access through a type is possible and safe. For this, rust provides the [`UnsafeCell`]
|
||||
/// escape hatch, which allows you to get a `*mut T` from a `&UnsafeCell<T>` and around which safe abstractions can be built.
|
||||
///
|
||||
/// Access to resources and components can be done uniquely using [`World::resource_mut`] and [`World::entity_mut`], and shared using [`World::resource`] and [`World::entity`].
|
||||
/// These methods use lifetimes to check at compile time that no aliasing rules are being broken.
|
||||
///
|
||||
/// This alone is not enough to implement bevy systems where multiple systems can access *disjoint* parts of the world concurrently. For this, bevy stores all values of
|
||||
/// resources and components (and [`ComponentTicks`](crate::component::ComponentTicks)) in [`UnsafeCell`](std::cell::UnsafeCell)s, and carefully validates disjoint access patterns using
|
||||
/// resources and components (and [`ComponentTicks`]) in [`UnsafeCell`]s, and carefully validates disjoint access patterns using
|
||||
/// APIs like [`System::component_access`](crate::system::System::component_access).
|
||||
///
|
||||
/// A system then can be executed using [`System::run_unsafe`](crate::system::System::run_unsafe) with a `&World` and use methods with interior mutability to access resource values.
|
||||
|
@ -156,7 +157,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
|||
// - caller ensures there is no `&mut World` this makes it okay to make a `&World`
|
||||
// - caller ensures there is no mutable borrows of world data, this means the caller cannot
|
||||
// misuse the returned `&World`
|
||||
unsafe { &*self.0 }
|
||||
unsafe { self.unsafe_world() }
|
||||
}
|
||||
|
||||
/// Gets a reference to the [`World`] this [`UnsafeWorldCell`] belong to.
|
||||
|
@ -185,7 +186,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
|||
/// - must not be used in a way that would conflict with any
|
||||
/// live exclusive borrows on world data
|
||||
#[inline]
|
||||
pub(crate) unsafe fn unsafe_world(self) -> &'w World {
|
||||
unsafe fn unsafe_world(self) -> &'w World {
|
||||
// SAFETY:
|
||||
// - caller ensures that the returned `&World` is not used in a way that would conflict
|
||||
// with any existing mutable borrows of world data
|
||||
|
@ -224,6 +225,13 @@ impl<'w> UnsafeWorldCell<'w> {
|
|||
&unsafe { self.world_metadata() }.components
|
||||
}
|
||||
|
||||
/// Retrieves this world's collection of [removed components](RemovedComponentEvents).
|
||||
pub fn removed_components(self) -> &'w RemovedComponentEvents {
|
||||
// SAFETY:
|
||||
// - we only access world metadata
|
||||
&unsafe { self.world_metadata() }.removed_components
|
||||
}
|
||||
|
||||
/// Retrieves this world's [`Bundles`] collection.
|
||||
#[inline]
|
||||
pub fn bundles(self) -> &'w Bundles {
|
||||
|
@ -901,7 +909,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get an untyped pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World).
|
||||
/// Get an untyped pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`].
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
|
@ -929,7 +937,7 @@ unsafe fn get_component(
|
|||
}
|
||||
}
|
||||
|
||||
/// Get an untyped pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`]
|
||||
/// Get an untyped pointer to a particular [`Component`] and its [`ComponentTicks`]
|
||||
///
|
||||
/// # Safety
|
||||
/// - `location` must refer to an archetype that contains `entity`
|
||||
|
|
|
@ -336,19 +336,19 @@ impl<'w> WorldCell<'w> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sends an [`Event`](crate::event::Event).
|
||||
/// Sends an [`Event`].
|
||||
#[inline]
|
||||
pub fn send_event<E: Event>(&self, event: E) {
|
||||
self.send_event_batch(std::iter::once(event));
|
||||
}
|
||||
|
||||
/// Sends the default value of the [`Event`](crate::event::Event) of type `E`.
|
||||
/// Sends the default value of the [`Event`] of type `E`.
|
||||
#[inline]
|
||||
pub fn send_event_default<E: Event + Default>(&self) {
|
||||
self.send_event_batch(std::iter::once(E::default()));
|
||||
}
|
||||
|
||||
/// Sends a batch of [`Event`](crate::event::Event)s from an iterator.
|
||||
/// Sends a batch of [`Event`]s from an iterator.
|
||||
#[inline]
|
||||
pub fn send_event_batch<E: Event>(&self, events: impl Iterator<Item = E>) {
|
||||
match self.get_resource_mut::<Events<E>>() {
|
||||
|
|
|
@ -2,7 +2,7 @@ error[E0502]: cannot borrow `e_mut` as mutable because it is also borrowed as im
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:17:26
|
||||
|
|
||||
16 | let gotten: &A = e_mut.get::<A>().unwrap();
|
||||
| ---------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
17 | let gotten2: A = e_mut.take::<A>().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
18 | assert_eq!(gotten, &gotten2); // oops UB
|
||||
|
@ -12,9 +12,9 @@ error[E0499]: cannot borrow `e_mut` as mutable more than once at a time
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:25:30
|
||||
|
|
||||
24 | let mut gotten: Mut<A> = e_mut.get_mut::<A>().unwrap();
|
||||
| -------------------- first mutable borrow occurs here
|
||||
| ----- first mutable borrow occurs here
|
||||
25 | let mut gotten2: A = e_mut.take::<A>().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^ second mutable borrow occurs here
|
||||
26 | assert_eq!(&mut *gotten, &mut gotten2); // oops UB
|
||||
| ------ first borrow later used here
|
||||
|
||||
|
@ -25,7 +25,7 @@ error[E0505]: cannot move out of `e_mut` because it is borrowed
|
|||
| --------- binding `e_mut` declared here
|
||||
...
|
||||
32 | let gotten: &A = e_mut.get::<A>().unwrap();
|
||||
| ---------------- borrow of `e_mut` occurs here
|
||||
| ----- borrow of `e_mut` occurs here
|
||||
33 | e_mut.despawn();
|
||||
| ^^^^^ move out of `e_mut` occurs here
|
||||
34 | assert_eq!(gotten, &A(Box::new(14_usize))); // oops UB
|
||||
|
@ -35,7 +35,7 @@ error[E0502]: cannot borrow `e_mut` as mutable because it is also borrowed as im
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:42:34
|
||||
|
|
||||
41 | let gotten: &A = e_mut.get::<A>().unwrap();
|
||||
| ---------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
42 | let gotten_mut: Mut<A> = e_mut.get_mut::<A>().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
43 | assert_eq!(gotten, &*gotten_mut); // oops UB
|
||||
|
@ -45,9 +45,9 @@ error[E0502]: cannot borrow `e_mut` as immutable because it is also borrowed as
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:48:26
|
||||
|
|
||||
47 | let gotten_mut: Mut<A> = e_mut.get_mut::<A>().unwrap();
|
||||
| -------------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
48 | let gotten: &A = e_mut.get::<A>().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
49 | assert_eq!(gotten, &*gotten_mut); // oops UB
|
||||
| ---------- mutable borrow later used here
|
||||
|
||||
|
@ -55,7 +55,7 @@ error[E0502]: cannot borrow `e_mut` as mutable because it is also borrowed as im
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:54:9
|
||||
|
|
||||
53 | let gotten: &A = e_mut.get::<A>().unwrap();
|
||||
| ---------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
54 | e_mut.insert::<B>(B);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
55 | assert_eq!(gotten, &A(Box::new(16_usize))); // oops UB
|
||||
|
@ -65,8 +65,8 @@ error[E0499]: cannot borrow `e_mut` as mutable more than once at a time
|
|||
--> tests/ui/entity_ref_mut_lifetime_safety.rs:61:9
|
||||
|
|
||||
60 | let mut gotten_mut: Mut<A> = e_mut.get_mut::<A>().unwrap();
|
||||
| -------------------- first mutable borrow occurs here
|
||||
| ----- first mutable borrow occurs here
|
||||
61 | e_mut.insert::<B>(B);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^ second mutable borrow occurs here
|
||||
62 | assert_eq!(&mut *gotten_mut, &mut A(Box::new(16_usize))); // oops UB
|
||||
| ---------- first borrow later used here
|
||||
|
|
|
@ -2,7 +2,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:17:39
|
||||
|
|
||||
16 | let data: &Foo = query.get(e).unwrap();
|
||||
| ------------ immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
17 | let mut data2: Mut<Foo> = query.get_mut(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
18 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -12,9 +12,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:23:30
|
||||
|
|
||||
22 | let mut data2: Mut<Foo> = query.get_mut(e).unwrap();
|
||||
| ---------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
23 | let data: &Foo = query.get(e).unwrap();
|
||||
| ^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
24 | assert_eq!(data, &mut *data2); // oops UB
|
||||
| ----- mutable borrow later used here
|
||||
|
||||
|
@ -22,7 +22,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:29:39
|
||||
|
|
||||
28 | let data: &Foo = query.get_component::<Foo>(e).unwrap();
|
||||
| ----------------------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
29 | let mut data2: Mut<Foo> = query.get_component_mut(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
30 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -32,9 +32,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:35:30
|
||||
|
|
||||
34 | let mut data2: Mut<Foo> = query.get_component_mut(e).unwrap();
|
||||
| -------------------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
35 | let data: &Foo = query.get_component::<Foo>(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
36 | assert_eq!(data, &mut *data2); // oops UB
|
||||
| ----- mutable borrow later used here
|
||||
|
||||
|
@ -42,7 +42,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:41:39
|
||||
|
|
||||
40 | let data: &Foo = query.single();
|
||||
| -------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
41 | let mut data2: Mut<Foo> = query.single_mut();
|
||||
| ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
42 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -52,9 +52,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:47:30
|
||||
|
|
||||
46 | let mut data2: Mut<Foo> = query.single_mut();
|
||||
| ------------------ mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
47 | let data: &Foo = query.single();
|
||||
| ^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
48 | assert_eq!(data, &mut *data2); // oops UB
|
||||
| ----- mutable borrow later used here
|
||||
|
||||
|
@ -62,7 +62,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:53:39
|
||||
|
|
||||
52 | let data: &Foo = query.get_single().unwrap();
|
||||
| ------------------ immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
53 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
54 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -72,9 +72,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:59:30
|
||||
|
|
||||
58 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
| ---------------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
59 | let data: &Foo = query.get_single().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
60 | assert_eq!(data, &mut *data2); // oops UB
|
||||
| ----- mutable borrow later used here
|
||||
|
||||
|
@ -82,7 +82,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:65:39
|
||||
|
|
||||
64 | let data: &Foo = query.iter().next().unwrap();
|
||||
| ------------ immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
65 | let mut data2: Mut<Foo> = query.iter_mut().next().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
66 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -92,9 +92,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:71:30
|
||||
|
|
||||
70 | let mut data2: Mut<Foo> = query.iter_mut().next().unwrap();
|
||||
| ---------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
71 | let data: &Foo = query.iter().next().unwrap();
|
||||
| ^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
72 | assert_eq!(data, &mut *data2); // oops UB
|
||||
| ----- mutable borrow later used here
|
||||
|
||||
|
@ -102,7 +102,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_lifetime_safety.rs:79:13
|
||||
|
|
||||
78 | query.for_each(|data| opt_data = Some(data));
|
||||
| -------------------------------------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
79 | query.for_each_mut(|data| opt_data_2 = Some(data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
80 | assert_eq!(opt_data.unwrap(), &mut *opt_data_2.unwrap()); // oops UB
|
||||
|
@ -112,8 +112,8 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_lifetime_safety.rs:87:13
|
||||
|
|
||||
86 | query.for_each_mut(|data| opt_data_2 = Some(data));
|
||||
| -------------------------------------------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
87 | query.for_each(|data| opt_data = Some(data));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
88 | assert_eq!(opt_data.unwrap(), &mut *opt_data_2.unwrap()); // oops UB
|
||||
| ---------- mutable borrow later used here
|
||||
|
|
|
@ -7,7 +7,7 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
| mutable borrow occurs here
|
||||
| mutable borrow later used here
|
||||
9 | for _ in query.to_readonly().iter() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
|
||||
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
|
||||
--> tests/ui/query_to_readonly.rs:14:18
|
||||
|
@ -24,10 +24,10 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/query_to_readonly.rs:39:30
|
||||
|
|
||||
36 | let mut mut_foo = query.single_mut();
|
||||
| ------------------ mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
...
|
||||
39 | let readonly_query = query.to_readonly();
|
||||
| ^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
...
|
||||
43 | *mut_foo = Foo;
|
||||
| ------- mutable borrow later used here
|
||||
|
@ -36,7 +36,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/query_to_readonly.rs:55:27
|
||||
|
|
||||
51 | let readonly_query = query.to_readonly();
|
||||
| ------------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
...
|
||||
55 | let mut mut_foo = query.single_mut();
|
||||
| ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
|
|
|
@ -2,9 +2,9 @@ error[E0499]: cannot borrow `query` as mutable more than once at a time
|
|||
--> tests/ui/system_query_get_lifetime_safety.rs:8:14
|
||||
|
|
||||
7 | let a1 = query.get_mut(e).unwrap();
|
||||
| ---------------- first mutable borrow occurs here
|
||||
| ----- first mutable borrow occurs here
|
||||
8 | let a2 = query.get_mut(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^ second mutable borrow occurs here
|
||||
9 | // this should fail to compile
|
||||
10 | println!("{} {}", a1.0, a2.0);
|
||||
| -- first borrow later used here
|
||||
|
|
|
@ -2,7 +2,7 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
|||
--> tests/ui/system_query_get_many_lifetime_safety.rs:8:14
|
||||
|
|
||||
7 | let a1 = query.get_many([e, e]).unwrap();
|
||||
| ---------------------- immutable borrow occurs here
|
||||
| ----- immutable borrow occurs here
|
||||
8 | let a2 = query.get_mut(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
9 | // this should fail to compile
|
||||
|
|
|
@ -2,9 +2,9 @@ error[E0499]: cannot borrow `query` as mutable more than once at a time
|
|||
--> tests/ui/system_query_get_many_mut_lifetime_safety.rs:8:14
|
||||
|
|
||||
7 | let a1 = query.get_many_mut([e, e]).unwrap();
|
||||
| -------------------------- first mutable borrow occurs here
|
||||
| ----- first mutable borrow occurs here
|
||||
8 | let a2 = query.get_mut(e).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^ second mutable borrow occurs here
|
||||
9 | // this should fail to compile
|
||||
10 | println!("{} {}", a1[0].0, a2.0);
|
||||
| ----- first borrow later used here
|
||||
|
|
|
@ -2,10 +2,10 @@ error[E0499]: cannot borrow `query` as mutable more than once at a time
|
|||
--> tests/ui/system_query_iter_lifetime_safety.rs:10:21
|
||||
|
|
||||
7 | let mut iter = query.iter_mut();
|
||||
| ---------------- first mutable borrow occurs here
|
||||
| ----- first mutable borrow occurs here
|
||||
...
|
||||
10 | let mut iter2 = query.iter_mut();
|
||||
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^ second mutable borrow occurs here
|
||||
...
|
||||
14 | println!("{}", a.0);
|
||||
| --- first borrow later used here
|
||||
|
|
|
@ -2,7 +2,7 @@ error[E0499]: cannot borrow `iter` as mutable more than once at a time
|
|||
--> tests/ui/system_query_iter_many_mut_lifetime_safety.rs:9:25
|
||||
|
|
||||
9 | while let Some(a) = iter.fetch_next() {
|
||||
| ^^^^^^^^^^^^^^^^^ `iter` was mutably borrowed here in the previous iteration of the loop
|
||||
| ^^^^ `iter` was mutably borrowed here in the previous iteration of the loop
|
||||
10 | // this should fail to compile
|
||||
11 | results.push(a);
|
||||
| --------------- first borrow used here, in later iteration of loop
|
||||
| ------- first borrow used here, in later iteration of loop
|
||||
|
|
|
@ -2,10 +2,10 @@ error[E0499]: cannot borrow `queries` as mutable more than once at a time
|
|||
--> tests/ui/system_query_set_get_lifetime_safety.rs:10:14
|
||||
|
|
||||
7 | let mut q2 = queries.p0();
|
||||
| ------------ first mutable borrow occurs here
|
||||
| ------- first mutable borrow occurs here
|
||||
...
|
||||
10 | let q1 = queries.p1();
|
||||
| ^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^^^ second mutable borrow occurs here
|
||||
...
|
||||
14 | b.0 = a.0
|
||||
| - first borrow later used here
|
||||
|
@ -14,10 +14,10 @@ error[E0499]: cannot borrow `queries` as mutable more than once at a time
|
|||
--> tests/ui/system_query_set_get_lifetime_safety.rs:21:18
|
||||
|
|
||||
18 | let q1 = queries.p1();
|
||||
| ------------ first mutable borrow occurs here
|
||||
| ------- first mutable borrow occurs here
|
||||
...
|
||||
21 | let mut q2 = queries.p0();
|
||||
| ^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^^^ second mutable borrow occurs here
|
||||
...
|
||||
25 | b.0 = a.0
|
||||
| --- first borrow later used here
|
||||
|
|
|
@ -2,10 +2,10 @@ error[E0499]: cannot borrow `queries` as mutable more than once at a time
|
|||
--> tests/ui/system_query_set_iter_lifetime_safety.rs:11:14
|
||||
|
|
||||
7 | let mut q2 = queries.p0();
|
||||
| ------------ first mutable borrow occurs here
|
||||
| ------- first mutable borrow occurs here
|
||||
...
|
||||
11 | let q1 = queries.p1();
|
||||
| ^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^^^ second mutable borrow occurs here
|
||||
...
|
||||
16 | b.0 = a.0
|
||||
| - first borrow later used here
|
||||
|
@ -14,10 +14,10 @@ error[E0499]: cannot borrow `queries` as mutable more than once at a time
|
|||
--> tests/ui/system_query_set_iter_lifetime_safety.rs:24:18
|
||||
|
|
||||
20 | let q1 = queries.p1();
|
||||
| ------------ first mutable borrow occurs here
|
||||
| ------- first mutable borrow occurs here
|
||||
...
|
||||
24 | let mut q2 = queries.p0();
|
||||
| ^^^^^^^^^^^^ second mutable borrow occurs here
|
||||
| ^^^^^^^ second mutable borrow occurs here
|
||||
...
|
||||
29 | b.0 = a.0;
|
||||
| --- first borrow later used here
|
||||
|
|
|
@ -2,10 +2,10 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
|||
--> tests/ui/system_state_iter_mut_overlap_safety.rs:18:13
|
||||
|
|
||||
15 | let mut_vec = query.iter_mut().collect::<Vec<bevy_ecs::prelude::Mut<A>>>();
|
||||
| ---------------- mutable borrow occurs here
|
||||
| ----- mutable borrow occurs here
|
||||
...
|
||||
18 | query.iter().collect::<Vec<&A>>(),
|
||||
| ^^^^^^^^^^^^ immutable borrow occurs here
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
...
|
||||
23 | mut_vec.iter().map(|m| **m).collect::<Vec<A>>(),
|
||||
| -------------- mutable borrow later used here
|
||||
| ------- mutable borrow later used here
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
//! Systems and type definitions for gamepad handling in Bevy.
|
||||
//!
|
||||
//! This crate is built on top of [GilRs](gilrs), a library
|
||||
//! that handles abstracting over platform-specific gamepad APIs.
|
||||
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod converter;
|
||||
mod gilrs_system;
|
||||
|
@ -12,6 +18,7 @@ use gilrs::GilrsBuilder;
|
|||
use gilrs_system::{gilrs_event_startup_system, gilrs_event_system};
|
||||
use rumble::{play_gilrs_rumble, RunningRumbleEffects};
|
||||
|
||||
/// Plugin that provides gamepad handling to an [`App`].
|
||||
#[derive(Default)]
|
||||
pub struct GilrsPlugin;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! A module for the [`Gizmos`](crate::gizmos::Gizmos) [`SystemParam`](bevy_ecs::system::SystemParam).
|
||||
//! A module for the [`Gizmos`] [`SystemParam`].
|
||||
|
||||
use std::{f32::consts::TAU, iter};
|
||||
|
||||
|
@ -23,7 +23,7 @@ pub(crate) struct GizmoStorage {
|
|||
pub strip_colors: Vec<ColorItem>,
|
||||
}
|
||||
|
||||
/// A [`SystemParam`](bevy_ecs::system::SystemParam) for drawing gizmos.
|
||||
/// A [`SystemParam`] for drawing gizmos.
|
||||
///
|
||||
/// They are drawn in immediate mode, which means they will be rendered only for
|
||||
/// the frames in which they are spawned.
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
//! # bevy_ecs::system::assert_is_system(system);
|
||||
//! ```
|
||||
//!
|
||||
//! See the documentation on [`Gizmos`](crate::gizmos::Gizmos) for more examples.
|
||||
//! See the documentation on [`Gizmos`] for more examples.
|
||||
|
||||
pub mod gizmos;
|
||||
|
||||
|
@ -149,7 +149,7 @@ pub struct GizmoConfig {
|
|||
pub line_width: f32,
|
||||
/// Apply perspective to gizmo lines.
|
||||
///
|
||||
/// This setting only affects 3D, non-orhographic cameras.
|
||||
/// This setting only affects 3D, non-orthographic cameras.
|
||||
///
|
||||
/// Defaults to `false`.
|
||||
pub line_perspective: bool,
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
//! Plugin providing an [`AssetLoader`](bevy_asset::AssetLoader) and type definitions
|
||||
//! for loading glTF 2.0 (a standard 3D scene definition format) files in Bevy.
|
||||
//!
|
||||
//! The [glTF 2.0 specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) defines the format of the glTF files.
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature = "bevy_animation")]
|
||||
use bevy_animation::AnimationClip;
|
||||
|
@ -27,6 +32,11 @@ pub struct GltfPlugin {
|
|||
}
|
||||
|
||||
impl GltfPlugin {
|
||||
/// Register a custom vertex attribute so that it is recognized when loading a glTF file with the [`GltfLoader`].
|
||||
///
|
||||
/// `name` must be the attribute name as found in the glTF data, which must start with an underscore.
|
||||
/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
|
||||
/// for additional details on custom attributes.
|
||||
pub fn add_custom_vertex_attribute(
|
||||
mut self,
|
||||
name: &str,
|
||||
|
@ -64,50 +74,81 @@ impl Plugin for GltfPlugin {
|
|||
/// Representation of a loaded glTF file.
|
||||
#[derive(Asset, Debug, TypePath)]
|
||||
pub struct Gltf {
|
||||
/// All scenes loaded from the glTF file.
|
||||
pub scenes: Vec<Handle<Scene>>,
|
||||
/// Named scenes loaded from the glTF file.
|
||||
pub named_scenes: HashMap<String, Handle<Scene>>,
|
||||
/// All meshes loaded from the glTF file.
|
||||
pub meshes: Vec<Handle<GltfMesh>>,
|
||||
/// Named meshes loaded from the glTF file.
|
||||
pub named_meshes: HashMap<String, Handle<GltfMesh>>,
|
||||
/// All materials loaded from the glTF file.
|
||||
pub materials: Vec<Handle<StandardMaterial>>,
|
||||
/// Named materials loaded from the glTF file.
|
||||
pub named_materials: HashMap<String, Handle<StandardMaterial>>,
|
||||
/// All nodes loaded from the glTF file.
|
||||
pub nodes: Vec<Handle<GltfNode>>,
|
||||
/// Named nodes loaded from the glTF file.
|
||||
pub named_nodes: HashMap<String, Handle<GltfNode>>,
|
||||
/// Default scene to be displayed.
|
||||
pub default_scene: Option<Handle<Scene>>,
|
||||
/// All animations loaded from the glTF file.
|
||||
#[cfg(feature = "bevy_animation")]
|
||||
pub animations: Vec<Handle<AnimationClip>>,
|
||||
/// Named animations loaded from the glTF file.
|
||||
#[cfg(feature = "bevy_animation")]
|
||||
pub named_animations: HashMap<String, Handle<AnimationClip>>,
|
||||
}
|
||||
|
||||
/// A glTF node with all of its child nodes, its [`GltfMesh`],
|
||||
/// [`Transform`](bevy_transform::prelude::Transform) and an optional [`GltfExtras`].
|
||||
///
|
||||
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-node).
|
||||
#[derive(Asset, Debug, Clone, TypePath)]
|
||||
pub struct GltfNode {
|
||||
/// Direct children of the node.
|
||||
pub children: Vec<GltfNode>,
|
||||
/// Mesh of the node.
|
||||
pub mesh: Option<Handle<GltfMesh>>,
|
||||
/// Local transform.
|
||||
pub transform: bevy_transform::prelude::Transform,
|
||||
/// Additional data.
|
||||
pub extras: Option<GltfExtras>,
|
||||
}
|
||||
|
||||
/// A glTF mesh, which may consist of multiple [`GltfPrimitives`](GltfPrimitive)
|
||||
/// and an optional [`GltfExtras`].
|
||||
///
|
||||
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-mesh).
|
||||
#[derive(Asset, Debug, Clone, TypePath)]
|
||||
pub struct GltfMesh {
|
||||
/// Primitives of the glTF mesh.
|
||||
pub primitives: Vec<GltfPrimitive>,
|
||||
/// Additional data.
|
||||
pub extras: Option<GltfExtras>,
|
||||
}
|
||||
|
||||
/// Part of a [`GltfMesh`] that consists of a [`Mesh`], an optional [`StandardMaterial`] and [`GltfExtras`].
|
||||
///
|
||||
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-mesh-primitive).
|
||||
#[derive(Asset, Debug, Clone, TypePath)]
|
||||
pub struct GltfPrimitive {
|
||||
/// Topology to be rendered.
|
||||
pub mesh: Handle<Mesh>,
|
||||
/// Material to apply to the `mesh`.
|
||||
pub material: Option<Handle<StandardMaterial>>,
|
||||
/// Additional data.
|
||||
pub extras: Option<GltfExtras>,
|
||||
/// Additional data of the `material`.
|
||||
pub material_extras: Option<GltfExtras>,
|
||||
}
|
||||
|
||||
/// Additional untyped data that can be present on most glTF types.
|
||||
///
|
||||
/// See [the relevant glTF specification section](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-extras).
|
||||
#[derive(Clone, Debug, Reflect, Default, Component)]
|
||||
#[reflect(Component)]
|
||||
pub struct GltfExtras {
|
||||
/// Content of the extra data.
|
||||
pub value: String,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{vertex_attributes::convert_attribute, Gltf, GltfExtras, GltfNode};
|
||||
use bevy_asset::{
|
||||
anyhow, io::Reader, AssetLoadError, AssetLoader, AsyncReadExt, Handle, LoadContext,
|
||||
ReadAssetBytesError,
|
||||
io::Reader, AssetLoadError, AssetLoader, AsyncReadExt, Handle, LoadContext, ReadAssetBytesError,
|
||||
};
|
||||
use bevy_core::Name;
|
||||
use bevy_core_pipeline::prelude::Camera3dBundle;
|
||||
|
@ -49,51 +48,76 @@ use thiserror::Error;
|
|||
/// An error that occurs when loading a glTF file.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GltfError {
|
||||
/// Unsupported primitive mode.
|
||||
#[error("unsupported primitive mode")]
|
||||
UnsupportedPrimitive { mode: Mode },
|
||||
UnsupportedPrimitive {
|
||||
/// The primitive mode.
|
||||
mode: Mode,
|
||||
},
|
||||
/// Invalid glTF file.
|
||||
#[error("invalid glTF file: {0}")]
|
||||
Gltf(#[from] gltf::Error),
|
||||
/// Binary blob is missing.
|
||||
#[error("binary blob is missing")]
|
||||
MissingBlob,
|
||||
/// Decoding the base64 mesh data failed.
|
||||
#[error("failed to decode base64 mesh data")]
|
||||
Base64Decode(#[from] base64::DecodeError),
|
||||
/// Unsupported buffer format.
|
||||
#[error("unsupported buffer format")]
|
||||
BufferFormatUnsupported,
|
||||
/// Invalid image mime type.
|
||||
#[error("invalid image mime type: {0}")]
|
||||
InvalidImageMimeType(String),
|
||||
/// Error when loading a texture. Might be due to a disabled image file format feature.
|
||||
#[error("You may need to add the feature for the file format: {0}")]
|
||||
ImageError(#[from] TextureError),
|
||||
/// Failed to read bytes from an asset path.
|
||||
#[error("failed to read bytes from an asset path: {0}")]
|
||||
ReadAssetBytesError(#[from] ReadAssetBytesError),
|
||||
/// Failed to load asset from an asset path.
|
||||
#[error("failed to load asset from an asset path: {0}")]
|
||||
AssetLoadError(#[from] AssetLoadError),
|
||||
/// Missing sampler for an animation.
|
||||
#[error("Missing sampler for animation {0}")]
|
||||
MissingAnimationSampler(usize),
|
||||
/// Failed to generate tangents.
|
||||
#[error("failed to generate tangents: {0}")]
|
||||
GenerateTangentsError(#[from] bevy_render::mesh::GenerateTangentsError),
|
||||
/// Failed to generate morph targets.
|
||||
#[error("failed to generate morph targets: {0}")]
|
||||
MorphTarget(#[from] bevy_render::mesh::morph::MorphBuildError),
|
||||
/// Failed to load a file.
|
||||
#[error("failed to load file: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// Loads glTF files with all of their data as their corresponding bevy representations.
|
||||
pub struct GltfLoader {
|
||||
/// List of compressed image formats handled by the loader.
|
||||
pub supported_compressed_formats: CompressedImageFormats,
|
||||
/// Custom vertex attributes that will be recognized when loading a glTF file.
|
||||
///
|
||||
/// Keys must be the attribute names as found in the glTF data, which must start with an underscore.
|
||||
/// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview)
|
||||
/// for additional details on custom attributes.
|
||||
pub custom_vertex_attributes: HashMap<String, MeshVertexAttribute>,
|
||||
}
|
||||
|
||||
impl AssetLoader for GltfLoader {
|
||||
type Asset = Gltf;
|
||||
type Settings = ();
|
||||
type Error = GltfError;
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
reader: &'a mut Reader,
|
||||
_settings: &'a (),
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> bevy_utils::BoxedFuture<'a, Result<Gltf, anyhow::Error>> {
|
||||
) -> bevy_utils::BoxedFuture<'a, Result<Gltf, Self::Error>> {
|
||||
Box::pin(async move {
|
||||
let mut bytes = Vec::new();
|
||||
reader.read_to_end(&mut bytes).await?;
|
||||
Ok(load_gltf(self, &bytes, load_context).await?)
|
||||
load_gltf(self, &bytes, load_context).await
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -263,6 +263,7 @@ pub(crate) fn convert_attribute(
|
|||
gltf::Semantic::Tangents => Some((Mesh::ATTRIBUTE_TANGENT, ConversionMode::Any)),
|
||||
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba)),
|
||||
gltf::Semantic::TexCoords(0) => Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord)),
|
||||
gltf::Semantic::TexCoords(1) => Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord)),
|
||||
gltf::Semantic::Joints(0) => {
|
||||
Some((Mesh::ATTRIBUTE_JOINT_INDEX, ConversionMode::JointIndex))
|
||||
}
|
||||
|
|
|
@ -664,12 +664,12 @@ mod tests {
|
|||
};
|
||||
|
||||
/// Assert the (non)existence and state of the child's [`Parent`] component.
|
||||
fn assert_parent(world: &mut World, child: Entity, parent: Option<Entity>) {
|
||||
fn assert_parent(world: &World, child: Entity, parent: Option<Entity>) {
|
||||
assert_eq!(world.get::<Parent>(child).map(|p| p.get()), parent);
|
||||
}
|
||||
|
||||
/// Assert the (non)existence and state of the parent's [`Children`] component.
|
||||
fn assert_children(world: &mut World, parent: Entity, children: Option<&[Entity]>) {
|
||||
fn assert_children(world: &World, parent: Entity, children: Option<&[Entity]>) {
|
||||
assert_eq!(world.get::<Children>(parent).map(|c| &**c), children);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ impl Parent {
|
|||
|
||||
/// Gets the parent [`Entity`] as a slice of length 1.
|
||||
///
|
||||
/// Useful for making APIs that require a type or homogenous storage
|
||||
/// Useful for making APIs that require a type or homogeneous storage
|
||||
/// for both [`Children`] & [`Parent`] that is agnostic to edge direction.
|
||||
///
|
||||
/// [`Children`]: super::children::Children
|
||||
|
|
|
@ -24,7 +24,7 @@ use bevy_ecs::schedule::State;
|
|||
///
|
||||
/// In case multiple systems are checking for [`Input::just_pressed`] or [`Input::just_released`]
|
||||
/// but only one should react, for example in the case of triggering
|
||||
/// [`State`](bevy_ecs::schedule::State) change, you should consider clearing the input state, either by:
|
||||
/// [`State`] change, you should consider clearing the input state, either by:
|
||||
///
|
||||
/// * Using [`Input::clear_just_pressed`] or [`Input::clear_just_released`] instead.
|
||||
/// * Calling [`Input::clear`] or [`Input::reset`] immediately after the state change.
|
||||
|
|
|
@ -19,7 +19,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
|||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// The event is consumed inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system)
|
||||
/// The event is consumed inside of the [`keyboard_input_system`]
|
||||
/// to update the [`Input<KeyCode>`](crate::Input<KeyCode>) resource.
|
||||
#[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
|
@ -70,16 +70,16 @@ pub fn keyboard_input_system(
|
|||
}
|
||||
}
|
||||
|
||||
/// The key code of a [`KeyboardInput`](crate::keyboard::KeyboardInput).
|
||||
/// The key code of a [`KeyboardInput`].
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// It is used as the generic `T` value of an [`Input`](crate::Input) to create a `Res<Input<KeyCode>>`.
|
||||
/// The resource values are mapped to the current layout of the keyboard and correlate to an [`ScanCode`](ScanCode).
|
||||
/// It is used as the generic `T` value of an [`Input`] to create a `Res<Input<KeyCode>>`.
|
||||
/// The resource values are mapped to the current layout of the keyboard and correlate to an [`ScanCode`].
|
||||
///
|
||||
/// ## Updating
|
||||
///
|
||||
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
|
||||
/// The resource is updated inside of the [`keyboard_input_system`].
|
||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Reflect)]
|
||||
#[reflect(Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(
|
||||
|
@ -443,16 +443,16 @@ pub enum KeyCode {
|
|||
Cut,
|
||||
}
|
||||
|
||||
/// The scan code of a [`KeyboardInput`](crate::keyboard::KeyboardInput).
|
||||
/// The scan code of a [`KeyboardInput`].
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// It is used as the generic `<T>` value of an [`Input`](crate::Input) to create a `Res<Input<ScanCode>>`.
|
||||
/// The resource values are mapped to the physical location of a key on the keyboard and correlate to an [`KeyCode`](KeyCode)
|
||||
/// It is used as the generic `<T>` value of an [`Input`] to create a `Res<Input<ScanCode>>`.
|
||||
/// The resource values are mapped to the physical location of a key on the keyboard and correlate to an [`KeyCode`]
|
||||
///
|
||||
/// ## Updating
|
||||
///
|
||||
/// The resource is updated inside of the [`keyboard_input_system`](crate::keyboard::keyboard_input_system).
|
||||
/// The resource is updated inside of the [`keyboard_input_system`].
|
||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Reflect)]
|
||||
#[reflect(Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(
|
||||
|
|
|
@ -19,7 +19,7 @@ use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
|
|||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// The event is read inside of the [`mouse_button_input_system`](crate::mouse::mouse_button_input_system)
|
||||
/// The event is read inside of the [`mouse_button_input_system`]
|
||||
/// to update the [`Input<MouseButton>`](crate::Input<MouseButton>) resource.
|
||||
#[derive(Event, Debug, Clone, Copy, PartialEq, Eq, Reflect)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
|
@ -41,12 +41,12 @@ pub struct MouseButtonInput {
|
|||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// It is used as the generic `T` value of an [`Input`](crate::Input) to create a `bevy`
|
||||
/// It is used as the generic `T` value of an [`Input`] to create a `bevy`
|
||||
/// resource.
|
||||
///
|
||||
/// ## Updating
|
||||
///
|
||||
/// The resource is updated inside of the [`mouse_button_input_system`](crate::mouse::mouse_button_input_system).
|
||||
/// The resource is updated inside of the [`mouse_button_input_system`].
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Reflect)]
|
||||
#[reflect(Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(
|
||||
|
@ -88,7 +88,7 @@ pub struct MouseMotion {
|
|||
|
||||
/// The scroll unit.
|
||||
///
|
||||
/// Describes how a value of a [`MouseWheel`](crate::mouse::MouseWheel) event has to be interpreted.
|
||||
/// Describes how a value of a [`MouseWheel`] event has to be interpreted.
|
||||
///
|
||||
/// The value of the event can either be interpreted as the amount of lines or the amount of pixels
|
||||
/// to scroll.
|
||||
|
@ -102,12 +102,12 @@ pub struct MouseMotion {
|
|||
pub enum MouseScrollUnit {
|
||||
/// The line scroll unit.
|
||||
///
|
||||
/// The delta of the associated [`MouseWheel`](crate::mouse::MouseWheel) event corresponds
|
||||
/// The delta of the associated [`MouseWheel`] event corresponds
|
||||
/// to the amount of lines or rows to scroll.
|
||||
Line,
|
||||
/// The pixel scroll unit.
|
||||
///
|
||||
/// The delta of the associated [`MouseWheel`](crate::mouse::MouseWheel) event corresponds
|
||||
/// The delta of the associated [`MouseWheel`] event corresponds
|
||||
/// to the amount of pixels to scroll.
|
||||
Pixel,
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ pub struct TouchInput {
|
|||
pub id: u64,
|
||||
}
|
||||
|
||||
/// A force description of a [`Touch`](crate::touch::Touch) input.
|
||||
/// A force description of a [`Touch`] input.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
|
||||
#[reflect(Debug, PartialEq)]
|
||||
#[cfg_attr(
|
||||
|
@ -92,7 +92,7 @@ pub enum ForceTouch {
|
|||
Normalized(f64),
|
||||
}
|
||||
|
||||
/// A phase of a [`TouchInput`](crate::touch::TouchInput).
|
||||
/// A phase of a [`TouchInput`].
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
|
@ -223,7 +223,7 @@ impl From<&TouchInput> for Touch {
|
|||
///
|
||||
/// ## Updating
|
||||
///
|
||||
/// The resource is updated inside of the [`touch_screen_input_system`](crate::touch::touch_screen_input_system).
|
||||
/// The resource is updated inside of the [`touch_screen_input_system`].
|
||||
#[derive(Debug, Clone, Default, Resource)]
|
||||
pub struct Touches {
|
||||
/// A collection of every [`Touch`] that is currently being pressed.
|
||||
|
|
|
@ -63,6 +63,7 @@ shader_format_spirv = ["bevy_render/shader_format_spirv"]
|
|||
|
||||
serialize = ["bevy_core/serialize", "bevy_input/serialize", "bevy_time/serialize", "bevy_window/serialize", "bevy_transform/serialize", "bevy_math/serialize", "bevy_scene?/serialize"]
|
||||
multi-threaded = ["bevy_asset/multi-threaded", "bevy_ecs/multi-threaded", "bevy_tasks/multi-threaded"]
|
||||
async-io = ["bevy_tasks/async-io"]
|
||||
|
||||
# Display server protocol support (X11 is enabled by default)
|
||||
wayland = ["bevy_winit/wayland"]
|
||||
|
@ -75,7 +76,7 @@ subpixel_glyph_atlas = ["bevy_text/subpixel_glyph_atlas"]
|
|||
pbr_transmission_textures = ["bevy_pbr?/pbr_transmission_textures", "bevy_gltf?/pbr_transmission_textures"]
|
||||
|
||||
# Optimise for WebGL2
|
||||
webgl = ["bevy_core_pipeline?/webgl", "bevy_pbr?/webgl", "bevy_render?/webgl", "bevy_gizmos?/webgl"]
|
||||
webgl = ["bevy_core_pipeline?/webgl", "bevy_pbr?/webgl", "bevy_render?/webgl", "bevy_gizmos?/webgl", "bevy_sprite?/webgl"]
|
||||
|
||||
# enable systems that allow for automated testing on CI
|
||||
bevy_ci_testing = ["bevy_app/bevy_ci_testing", "bevy_time/bevy_ci_testing", "bevy_render?/bevy_ci_testing", "bevy_render?/ci_limits"]
|
||||
|
|
|
@ -64,7 +64,7 @@ impl PluginGroup for DefaultPlugins {
|
|||
|
||||
#[cfg(feature = "bevy_winit")]
|
||||
{
|
||||
group = group.add(bevy_winit::WinitPlugin);
|
||||
group = group.add(bevy_winit::WinitPlugin::default());
|
||||
}
|
||||
|
||||
#[cfg(feature = "bevy_render")]
|
||||
|
|
|
@ -13,3 +13,4 @@ toml_edit = "0.19"
|
|||
syn = "2.0"
|
||||
quote = "1.0"
|
||||
rustc-hash = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
|
|
@ -2,6 +2,7 @@ use syn::{Expr, ExprLit, Lit};
|
|||
|
||||
use crate::symbol::Symbol;
|
||||
|
||||
/// Get a [literal string](struct@syn::LitStr) from the provided [expression](Expr).
|
||||
pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr> {
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Str(lit), ..
|
||||
|
@ -16,6 +17,7 @@ pub fn get_lit_str(attr_name: Symbol, value: &Expr) -> syn::Result<&syn::LitStr>
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a [literal boolean](struct@syn::LitBool) from the provided [expression](Expr) as a [`bool`].
|
||||
pub fn get_lit_bool(attr_name: Symbol, value: &Expr) -> syn::Result<bool> {
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Bool(lit),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue