Skip to content

Commit 992681b

Browse files
tim-blackbirdalice-i-cecilecart
committed
Make Resource trait opt-in, requiring #[derive(Resource)] V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.* # Objective Follow-up to #2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds. While ergonomic, this results in several drawbacks: * it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource * it is challenging to discover if a type is intended to be used as a resource * we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component). * dependencies can use the same Rust type as a resource in invisibly conflicting ways * raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values * we cannot capture a definitive list of possible resources to display to users in an editor ## Notes to reviewers * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits. *ira: My commits are not as well organized :')* * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does. * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with #4981. ## Changelog `Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro. ## Migration Guide Add `#[derive(Resource)]` to all types you are using as a resource. If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics. `ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing. Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead. Co-authored-by: Alice <alice.i.cecile@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: devil-ira <justthecooldude@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
1 parent 2ac7443 commit 992681b

File tree

133 files changed

+804
-524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+804
-524
lines changed

benches/benches/bevy_ecs/scheduling/run_criteria.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bevy_ecs::{
22
component::Component,
3-
prelude::{ParallelSystemDescriptorCoercion, Res, RunCriteriaDescriptorCoercion},
3+
prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion},
44
schedule::{ShouldRun, Stage, SystemStage},
55
system::Query,
66
world::World,
@@ -136,7 +136,7 @@ pub fn run_criteria_no_with_labels(criterion: &mut Criterion) {
136136
group.finish();
137137
}
138138

139-
#[derive(Component)]
139+
#[derive(Component, Resource)]
140140
struct TestBool(pub bool);
141141

142142
pub fn run_criteria_yes_with_query(criterion: &mut Criterion) {

crates/bevy_app/src/app.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{CoreStage, Plugin, PluginGroup, PluginGroupBuilder, StartupSchedule, StartupStage};
22
pub use bevy_derive::AppLabel;
3+
use bevy_derive::{Deref, DerefMut};
34
use bevy_ecs::{
45
event::{Event, Events},
56
prelude::{FromWorld, IntoExclusiveSystem},
@@ -22,6 +23,11 @@ bevy_utils::define_label!(
2223
AppLabelId,
2324
);
2425

26+
/// The [`Resource`] that stores the [`App`]'s [`TypeRegistry`](bevy_reflect::TypeRegistry).
27+
#[cfg(feature = "bevy_reflect")]
28+
#[derive(Resource, Clone, Deref, DerefMut, Default)]
29+
pub struct AppTypeRegistry(pub bevy_reflect::TypeRegistryArc);
30+
2531
#[allow(clippy::needless_doctest_main)]
2632
/// A container of app logic and data.
2733
///
@@ -74,7 +80,7 @@ impl Default for App {
7480
fn default() -> Self {
7581
let mut app = App::empty();
7682
#[cfg(feature = "bevy_reflect")]
77-
app.init_resource::<bevy_reflect::TypeRegistryArc>();
83+
app.init_resource::<AppTypeRegistry>();
7884

7985
app.add_default_stages()
8086
.add_event::<AppExit>()
@@ -647,7 +653,9 @@ impl App {
647653
///
648654
/// ```
649655
/// # use bevy_app::prelude::*;
656+
/// # use bevy_ecs::prelude::*;
650657
/// #
658+
/// #[derive(Resource)]
651659
/// struct MyCounter {
652660
/// counter: usize,
653661
/// }
@@ -660,15 +668,16 @@ impl App {
660668
self
661669
}
662670

663-
/// Inserts a non-send [`Resource`] to the app.
671+
/// Inserts a non-send resource to the app.
664672
///
665673
/// You usually want to use [`insert_resource`](Self::insert_resource),
666-
/// but there are some special cases when a [`Resource`] cannot be sent across threads.
674+
/// but there are some special cases when a resource cannot be sent across threads.
667675
///
668676
/// # Examples
669677
///
670678
/// ```
671679
/// # use bevy_app::prelude::*;
680+
/// # use bevy_ecs::prelude::*;
672681
/// #
673682
/// struct MyCounter {
674683
/// counter: usize,
@@ -694,7 +703,9 @@ impl App {
694703
///
695704
/// ```
696705
/// # use bevy_app::prelude::*;
706+
/// # use bevy_ecs::prelude::*;
697707
/// #
708+
/// #[derive(Resource)]
698709
/// struct MyCounter {
699710
/// counter: usize,
700711
/// }
@@ -873,7 +884,7 @@ impl App {
873884
#[cfg(feature = "bevy_reflect")]
874885
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
875886
{
876-
let registry = self.world.resource_mut::<bevy_reflect::TypeRegistryArc>();
887+
let registry = self.world.resource_mut::<AppTypeRegistry>();
877888
registry.write().register::<T>();
878889
}
879890
self
@@ -906,7 +917,7 @@ impl App {
906917
&mut self,
907918
) -> &mut Self {
908919
{
909-
let registry = self.world.resource_mut::<bevy_reflect::TypeRegistryArc>();
920+
let registry = self.world.resource_mut::<AppTypeRegistry>();
910921
registry.write().register_type_data::<T, D>();
911922
}
912923
self

crates/bevy_app/src/ci_testing.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use crate::{app::AppExit, App};
22
use serde::Deserialize;
33

4+
use bevy_ecs::prelude::Resource;
45
use bevy_utils::tracing::info;
56

67
/// A configuration struct for automated CI testing.
78
///
89
/// It gets used when the `bevy_ci_testing` feature is enabled to automatically
910
/// exit a Bevy app when run through the CI. This is needed because otherwise
1011
/// Bevy apps would be stuck in the game loop and wouldn't allow the CI to progress.
11-
#[derive(Deserialize)]
12+
#[derive(Deserialize, Resource)]
1213
pub struct CiTestingConfig {
1314
/// The number of frames after which Bevy should exit.
1415
pub exit_after: Option<u32>,

crates/bevy_app/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub use schedule_runner::*;
1818

1919
#[allow(missing_docs)]
2020
pub mod prelude {
21+
#[cfg(feature = "bevy_reflect")]
22+
#[doc(hidden)]
23+
pub use crate::AppTypeRegistry;
2124
#[doc(hidden)]
2225
pub use crate::{
2326
app::App, CoreStage, DynamicPlugin, Plugin, PluginGroup, StartupSchedule, StartupStage,

crates/bevy_app/src/schedule_runner.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{
33
plugin::Plugin,
44
};
55
use bevy_ecs::event::{Events, ManualEventReader};
6+
use bevy_ecs::prelude::Resource;
67
use bevy_utils::{Duration, Instant};
78

89
#[cfg(target_arch = "wasm32")]
@@ -34,7 +35,7 @@ impl Default for RunMode {
3435
/// The configuration information for the [`ScheduleRunnerPlugin`].
3536
///
3637
/// It gets added as a [`Resource`](bevy_ecs::system::Resource) inside of the [`ScheduleRunnerPlugin`].
37-
#[derive(Copy, Clone, Default)]
38+
#[derive(Copy, Clone, Default, Resource)]
3839
pub struct ScheduleRunnerSettings {
3940
/// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly.
4041
pub run_mode: RunMode,

crates/bevy_asset/src/asset_server.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
RefChange, RefChangeChannel, SourceInfo, SourceMeta,
66
};
77
use anyhow::Result;
8-
use bevy_ecs::system::{Res, ResMut};
8+
use bevy_ecs::system::{Res, ResMut, Resource};
99
use bevy_log::warn;
1010
use bevy_tasks::IoTaskPool;
1111
use bevy_utils::{Entry, HashMap, Uuid};
@@ -102,7 +102,7 @@ pub struct AssetServerInternal {
102102
/// See the [`asset_loading`] example for more information.
103103
///
104104
/// [`asset_loading`]: https://github.com/bevyengine/bevy/tree/latest/examples/asset/asset_loading.rs
105-
#[derive(Clone)]
105+
#[derive(Clone, Resource)]
106106
pub struct AssetServer {
107107
pub(crate) server: Arc<AssetServerInternal>,
108108
}

crates/bevy_asset/src/assets.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
use bevy_app::App;
66
use bevy_ecs::{
77
event::{EventWriter, Events},
8-
system::ResMut,
8+
system::{ResMut, Resource},
99
world::FromWorld,
1010
};
1111
use bevy_utils::HashMap;
@@ -66,7 +66,7 @@ impl<T: Asset> Debug for AssetEvent<T> {
6666
/// Remember, if there are no Strong handles for an asset (i.e. they have all been dropped), the
6767
/// asset will unload. Make sure you always have a Strong handle when you want to keep an asset
6868
/// loaded!
69-
#[derive(Debug)]
69+
#[derive(Debug, Resource)]
7070
pub struct Assets<T: Asset> {
7171
assets: HashMap<HandleId, T>,
7272
events: Events<AssetEvent<T>>,

crates/bevy_asset/src/debug_asset_server.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use bevy_app::{App, Plugin};
66
use bevy_ecs::{
77
event::Events,
88
schedule::SystemLabel,
9-
system::{NonSendMut, Res, ResMut, SystemState},
9+
system::{NonSendMut, Res, ResMut, Resource, SystemState},
1010
};
1111
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
1212
use bevy_utils::HashMap;
@@ -52,6 +52,7 @@ pub struct DebugAssetServerPlugin;
5252

5353
/// A collection that maps internal assets in a [`DebugAssetApp`]'s asset server to their mirrors in
5454
/// the main [`App`].
55+
#[derive(Resource)]
5556
pub struct HandleMap<T: Asset> {
5657
/// The collection of asset handles.
5758
pub handles: HashMap<Handle<T>, Handle<T>>,

crates/bevy_asset/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ pub use loader::*;
4343
pub use path::*;
4444

4545
use bevy_app::{prelude::Plugin, App};
46-
use bevy_ecs::schedule::{StageLabel, SystemStage};
46+
use bevy_ecs::{
47+
schedule::{StageLabel, SystemStage},
48+
system::Resource,
49+
};
4750

4851
/// The names of asset stages in an [`App`] schedule.
4952
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
@@ -62,6 +65,7 @@ pub enum AssetStage {
6265
pub struct AssetPlugin;
6366

6467
/// [`AssetServer`] settings.
68+
#[derive(Resource)]
6569
pub struct AssetServerSettings {
6670
/// The base folder where assets are loaded from, relative to the executable.
6771
pub asset_folder: String,

crates/bevy_audio/src/audio.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::{AudioSink, AudioSource, Decodable};
22
use bevy_asset::{Asset, Handle, HandleId};
3+
use bevy_ecs::system::Resource;
34
use parking_lot::RwLock;
45
use std::{collections::VecDeque, fmt};
56

6-
/// Use this resource to play audio
7+
/// Use this [`Resource`] to play audio.
78
///
89
/// ```
910
/// # use bevy_ecs::system::Res;
@@ -13,6 +14,7 @@ use std::{collections::VecDeque, fmt};
1314
/// audio.play(asset_server.load("my_sound.ogg"));
1415
/// }
1516
/// ```
17+
#[derive(Resource)]
1618
pub struct Audio<Source = AudioSource>
1719
where
1820
Source: Asset + Decodable,

crates/bevy_core/src/task_pool_options.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bevy_ecs::prelude::Resource;
12
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
23
use bevy_utils::tracing::trace;
34

@@ -33,7 +34,7 @@ impl TaskPoolThreadAssignmentPolicy {
3334
/// Helper for configuring and creating the default task pools. For end-users who want full control,
3435
/// insert the default task pools into the resource map manually. If the pools are already inserted,
3536
/// this helper will do nothing.
36-
#[derive(Clone)]
37+
#[derive(Clone, Resource)]
3738
pub struct DefaultTaskPoolOptions {
3839
/// If the number of physical cores is less than min_total_threads, force using
3940
/// min_total_threads

crates/bevy_core_pipeline/src/clear_color.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ pub enum ClearColorConfig {
1313
None,
1414
}
1515

16-
/// When used as a resource, sets the color that is used to clear the screen between frames.
16+
/// A [`Resource`] that stores the color that is used to clear the screen between frames.
1717
///
18-
/// This color appears as the "background" color for simple apps, when
19-
/// there are portions of the screen with nothing rendered.
20-
#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)]
18+
/// This color appears as the "background" color for simple apps,
19+
/// when there are portions of the screen with nothing rendered.
20+
#[derive(Resource, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)]
2121
#[reflect(Resource)]
2222
pub struct ClearColor(pub Color);
2323

crates/bevy_diagnostic/src/diagnostic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bevy_ecs::system::Resource;
12
use bevy_log::warn;
23
use bevy_utils::{Duration, Instant, StableHashMap, Uuid};
34
use std::{borrow::Cow, collections::VecDeque};
@@ -154,7 +155,7 @@ impl Diagnostic {
154155
}
155156

156157
/// A collection of [Diagnostic]s
157-
#[derive(Debug, Default)]
158+
#[derive(Debug, Default, Resource)]
158159
pub struct Diagnostics {
159160
// This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between
160161
// runs when all diagnostics are inserted in the same order.

crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::{Diagnostic, DiagnosticId, Diagnostics};
22
use bevy_app::prelude::*;
3-
use bevy_ecs::system::{Res, ResMut};
3+
use bevy_ecs::system::{Res, ResMut, Resource};
44
use bevy_time::Time;
55

66
/// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count"
77
#[derive(Default)]
88
pub struct FrameTimeDiagnosticsPlugin;
99

10+
#[derive(Resource)]
1011
pub struct FrameTimeDiagnosticsState {
1112
frame_count: u64,
1213
}

crates/bevy_diagnostic/src/log_diagnostics_plugin.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{Diagnostic, DiagnosticId, Diagnostics};
22
use bevy_app::prelude::*;
3-
use bevy_ecs::system::{Res, ResMut};
3+
use bevy_ecs::system::{Res, ResMut, Resource};
44
use bevy_log::{debug, info};
55
use bevy_time::{Time, Timer};
66
use bevy_utils::Duration;
@@ -13,6 +13,7 @@ pub struct LogDiagnosticsPlugin {
1313
}
1414

1515
/// State used by the [`LogDiagnosticsPlugin`]
16+
#[derive(Resource)]
1617
struct LogDiagnosticsState {
1718
timer: Timer,
1819
filter: Option<Vec<DiagnosticId>>,

crates/bevy_ecs/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Apps often require unique resources, such as asset collections, renderers, audio
9191
```rust
9292
use bevy_ecs::prelude::*;
9393

94-
#[derive(Default)]
94+
#[derive(Resource, Default)]
9595
struct Time {
9696
seconds: f32,
9797
}
@@ -213,6 +213,7 @@ Resources also expose change state:
213213
```rust
214214
use bevy_ecs::prelude::*;
215215

216+
#[derive(Resource)]
216217
struct Time(f32);
217218

218219
// Prints "time changed!" if the Time resource has changed since the last run of the System

crates/bevy_ecs/examples/change_detection.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fn main() {
4040
}
4141

4242
// This struct will be used as a Resource keeping track of the total amount of spawned entities
43-
#[derive(Debug)]
43+
#[derive(Debug, Resource)]
4444
struct EntityCounter {
4545
pub value: i32,
4646
}

crates/bevy_ecs/examples/resources.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() {
2727
}
2828

2929
// Counter resource to be increased and read by systems
30-
#[derive(Debug)]
30+
#[derive(Debug, Resource)]
3131
struct Counter {
3232
pub value: i32,
3333
}

crates/bevy_ecs/macros/src/component.rs

+18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ use proc_macro2::{Span, TokenStream as TokenStream2};
44
use quote::{quote, ToTokens};
55
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
66

7+
pub fn derive_resource(input: TokenStream) -> TokenStream {
8+
let mut ast = parse_macro_input!(input as DeriveInput);
9+
let bevy_ecs_path: Path = crate::bevy_ecs_path();
10+
11+
ast.generics
12+
.make_where_clause()
13+
.predicates
14+
.push(parse_quote! { Self: Send + Sync + 'static });
15+
16+
let struct_name = &ast.ident;
17+
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
18+
19+
TokenStream::from(quote! {
20+
impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause {
21+
}
22+
})
23+
}
24+
725
pub fn derive_component(input: TokenStream) -> TokenStream {
826
let mut ast = parse_macro_input!(input as DeriveInput);
927
let bevy_ecs_path: Path = crate::bevy_ecs_path();

crates/bevy_ecs/macros/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,11 @@ pub(crate) fn bevy_ecs_path() -> syn::Path {
496496
BevyManifest::default().get_path("bevy_ecs")
497497
}
498498

499+
#[proc_macro_derive(Resource)]
500+
pub fn derive_resource(input: TokenStream) -> TokenStream {
501+
component::derive_resource(input)
502+
}
503+
499504
#[proc_macro_derive(Component, attributes(component))]
500505
pub fn derive_component(input: TokenStream) -> TokenStream {
501506
component::derive_component(input)

0 commit comments

Comments
 (0)