mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
c89541a1af
# Objective
There's a lot of code duplication in `access.rs`. The same logic is
duplicated between components and resources. This also takes up
unnecessary memory in `Access`, as it relies on bitsets spanning the
entire `ComponentId` range.
## Solution
Since resources are now a special kind of component, this can be
removed.
## Limitations
Since `!Send` data queries used `Access` resources, `!Send` data queries
now conflict with broad queries.
```rust
// 0.18
fn system(q1_: Query<EntityMut>, q2_: NonSend<R>) {} // valid, does not conflict
// 0.19
fn system(q1_: Query<EntityMut>, q2_: NonSend<R>) {} // invalid, does conflict
```
Given how rarely non-send data is used, I recommend using
```
// 0.19
fn system(q1_: Query<EntityMut, Without<R>>, q2_: NonSend<R>) {} // works again
```
If this is also unacceptable, this PR is blocked on the `!Send` data
removal from the ECS (or some hacky workaround).
## Extra Attention
@chescock brought `AssetChanged` to my attention. It has a weird access
pattern. See the following example:
```rust
fn system(c: Query<&mut AssetChanges<Mesh>>, r: Query<(), AssetChanged<Mesh>>) {}
```
System `c` registers access with `add_write` for `AssetChanges<Mesh>`,
while `r` registers access with `add_read` for both `Mesh` and
`AssetChanges<Mesh>`. This system is invalid, and I've added a test to
reflect that. However, since this stuff is tricky, I would like some
extra eyes on it. Currently, it looks *fine*.
81 lines
2.8 KiB
Markdown
81 lines
2.8 KiB
Markdown
# B0002
|
|
|
|
To keep [Rust rules on references](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references) (either one mutable reference or any number of immutable references) on a resource, it is not possible to have more than one resource of a kind if one is mutable in the same system. This can happen between [`Res<T>`](https://docs.rs/bevy/*/bevy/ecs/system/struct.Res.html) and [`ResMut<T>`](https://docs.rs/bevy/*/bevy/ecs/system/struct.ResMut.html) for the same `T`, or between [`NonSend<T>`](https://docs.rs/bevy/*/bevy/ecs/system/struct.NonSend.html) and [`NonSendMut<T>`](https://docs.rs/bevy/*/bevy/ecs/system/struct.NonSendMut.html) for the same `T`.
|
|
|
|
Erroneous code example:
|
|
|
|
```rust,should_panic
|
|
use bevy::prelude::*;
|
|
|
|
fn update_materials(
|
|
mut material_updater: ResMut<Assets<StandardMaterial>>,
|
|
current_materials: Res<Assets<StandardMaterial>>,
|
|
) {
|
|
// ...
|
|
}
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_systems(Update, update_materials)
|
|
.run();
|
|
}
|
|
```
|
|
|
|
This will panic, as it's not possible to have both a mutable and an immutable resource on `Assets<StandardMaterial>` at the same time.
|
|
|
|
As a mutable resource already provides access to the current resource value, so you can remove the immutable resource.
|
|
|
|
```rust,no_run
|
|
use bevy::prelude::*;
|
|
|
|
fn update_materials(
|
|
mut material_updater: ResMut<Assets<StandardMaterial>>,
|
|
) {
|
|
// ...
|
|
}
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_systems(Update, update_materials)
|
|
.run();
|
|
}
|
|
```
|
|
|
|
## Resources as Components
|
|
|
|
Under the hood, resources *are* components. The consequences of this are usually nothing more than an implementation detail, however, you may run into an issue where the following system throws an error:
|
|
|
|
```rust,no_run
|
|
# use bevy::prelude::*;
|
|
# #[derive(Resource)]
|
|
# struct MyResource;
|
|
fn system(all_entities: Query<EntityMut>, res: Res<MyResource>) {}
|
|
```
|
|
|
|
It's not possible to both query all entities and a resource when resources are stored on an entity.
|
|
To fix this, you should either constrain the `all_entities` query (you usually don't need to query *every* single entity), or exclude `MyResource` with `Without<MyResource>` or `Without<IsResource>`.
|
|
Here, `IsResource`, is a marker component on every single entity that holds a resource, so it's very convenient if you want to exclude resources.
|
|
|
|
Queries that might conflict with `Res` or `ResMut` include:
|
|
|
|
- `Query<()>`
|
|
- `Query<Entity>`
|
|
- `Query<EntityMut>`
|
|
- `Query<EntityRef>`
|
|
- `Query<EntityMutExcept>`
|
|
- `Query<EntityRefExcept>`
|
|
- `Query<Option<&T>>`
|
|
|
|
You can run into the same problem with non-send data:
|
|
|
|
```rust,no_run
|
|
# use bevy::prelude::*;
|
|
# #[derive(Resource)]
|
|
# struct MyNonSend;
|
|
fn system(all_entities: Query<EntityMut>, some_non_send: NonSend<MyNonSend>) {}
|
|
```
|
|
|
|
This can be fixed by adding a `Without<MyNonSend>` filter to the query.
|