From fee9d53c9e8f8d94fc412926aeb6c18eb53a9678 Mon Sep 17 00:00:00 2001 From: ira Date: Mon, 8 Aug 2022 21:36:35 +0000 Subject: [PATCH] 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 https://github.com/bevyengine/bevy/pull/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 https://github.com/bevyengine/bevy/issues/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 Co-authored-by: Alice Cecile Co-authored-by: devil-ira Co-authored-by: Carter Anderson --- .../bevy_ecs/scheduling/run_criteria.rs | 4 +- crates/bevy_app/src/app.rs | 21 +- crates/bevy_app/src/ci_testing.rs | 3 +- crates/bevy_app/src/lib.rs | 3 + crates/bevy_app/src/schedule_runner.rs | 3 +- crates/bevy_asset/src/asset_server.rs | 4 +- crates/bevy_asset/src/assets.rs | 4 +- crates/bevy_asset/src/debug_asset_server.rs | 3 +- crates/bevy_asset/src/lib.rs | 6 +- crates/bevy_audio/src/audio.rs | 4 +- crates/bevy_core/src/task_pool_options.rs | 3 +- crates/bevy_core_pipeline/src/clear_color.rs | 8 +- crates/bevy_diagnostic/src/diagnostic.rs | 3 +- .../src/frame_time_diagnostics_plugin.rs | 3 +- .../src/log_diagnostics_plugin.rs | 3 +- crates/bevy_ecs/README.md | 3 +- crates/bevy_ecs/examples/change_detection.rs | 2 +- crates/bevy_ecs/examples/resources.rs | 2 +- crates/bevy_ecs/macros/src/component.rs | 18 ++ crates/bevy_ecs/macros/src/lib.rs | 5 + crates/bevy_ecs/src/change_detection.rs | 10 +- crates/bevy_ecs/src/event.rs | 4 +- crates/bevy_ecs/src/lib.rs | 89 +++++---- .../src/schedule/executor_parallel.rs | 46 +++-- crates/bevy_ecs/src/schedule/stage.rs | 179 +++++++++--------- crates/bevy_ecs/src/schedule/state.rs | 99 ++++++---- crates/bevy_ecs/src/system/commands/mod.rs | 30 +-- .../bevy_ecs/src/system/exclusive_system.rs | 28 +-- crates/bevy_ecs/src/system/function_system.rs | 8 +- crates/bevy_ecs/src/system/mod.rs | 109 +++++++---- crates/bevy_ecs/src/system/query.rs | 8 +- crates/bevy_ecs/src/system/system_chaining.rs | 1 + crates/bevy_ecs/src/system/system_param.rs | 67 +++++-- crates/bevy_ecs/src/world/mod.rs | 47 +---- crates/bevy_ecs/src/world/world_cell.rs | 74 ++++---- .../ui/query_iter_many_mut_iterator_safety.rs | 4 +- .../ui/system_query_get_lifetime_safety.rs | 6 +- .../system_query_get_lifetime_safety.stderr | 8 +- .../system_query_get_many_lifetime_safety.rs | 6 +- ...stem_query_get_many_lifetime_safety.stderr | 8 +- ...stem_query_get_many_mut_lifetime_safety.rs | 6 +- ..._query_get_many_mut_lifetime_safety.stderr | 8 +- ...tem_query_iter_many_mut_lifetime_safety.rs | 4 +- .../system_query_set_get_lifetime_safety.rs | 12 +- crates/bevy_input/src/axis.rs | 3 +- crates/bevy_input/src/gamepad.rs | 27 +-- crates/bevy_input/src/input.rs | 3 +- crates/bevy_input/src/touch.rs | 4 +- crates/bevy_log/Cargo.toml | 1 + crates/bevy_log/src/lib.rs | 3 + crates/bevy_pbr/Cargo.toml | 1 + crates/bevy_pbr/src/light.rs | 8 +- crates/bevy_pbr/src/material.rs | 14 +- crates/bevy_pbr/src/render/light.rs | 4 +- crates/bevy_pbr/src/render/mesh.rs | 6 +- crates/bevy_pbr/src/wireframe.rs | 3 +- crates/bevy_render/src/extract_component.rs | 1 + crates/bevy_render/src/lib.rs | 12 +- crates/bevy_render/src/render_asset.rs | 12 +- crates/bevy_render/src/render_graph/graph.rs | 4 +- crates/bevy_render/src/render_phase/draw.rs | 4 +- .../src/render_resource/pipeline_cache.rs | 3 +- .../render_resource/pipeline_specializer.rs | 3 + crates/bevy_render/src/renderer/mod.rs | 21 +- .../bevy_render/src/renderer/render_device.rs | 3 +- crates/bevy_render/src/settings.rs | 3 +- .../bevy_render/src/texture/fallback_image.rs | 4 +- crates/bevy_render/src/texture/image.rs | 5 +- .../bevy_render/src/texture/texture_cache.rs | 4 +- crates/bevy_render/src/view/mod.rs | 4 +- crates/bevy_render/src/view/window.rs | 6 +- crates/bevy_scene/src/dynamic_scene.rs | 3 +- crates/bevy_scene/src/scene_loader.rs | 5 +- crates/bevy_scene/src/scene_spawner.rs | 8 +- crates/bevy_sprite/Cargo.toml | 1 + crates/bevy_sprite/src/mesh2d/material.rs | 14 +- crates/bevy_sprite/src/mesh2d/mesh.rs | 3 +- crates/bevy_sprite/src/render/mod.rs | 8 +- crates/bevy_text/src/pipeline.rs | 2 + crates/bevy_time/src/fixed_timestep.rs | 19 +- crates/bevy_time/src/lib.rs | 3 + crates/bevy_time/src/time.rs | 5 +- crates/bevy_ui/src/flex/mod.rs | 3 +- crates/bevy_ui/src/render/mod.rs | 5 +- crates/bevy_ui/src/render/pipeline.rs | 1 + crates/bevy_window/src/lib.rs | 3 +- crates/bevy_window/src/window.rs | 3 +- crates/bevy_window/src/windows.rs | 3 +- crates/bevy_winit/src/lib.rs | 2 +- crates/bevy_winit/src/web_resize.rs | 3 +- crates/bevy_winit/src/winit_config.rs | 3 +- errors/B0003.md | 1 + examples/2d/mesh2d_manual.rs | 1 + examples/2d/texture_atlas.rs | 2 +- examples/3d/skybox.rs | 1 + examples/animation/animated_fox.rs | 1 + examples/app/custom_loop.rs | 1 + examples/app/plugin.rs | 1 + examples/asset/custom_asset.rs | 2 +- examples/async_tasks/async_compute.rs | 5 +- .../external_source_external_thread.rs | 4 +- examples/audio/audio_control.rs | 1 + examples/ecs/ecs_guide.rs | 3 +- examples/ecs/event.rs | 1 + examples/ecs/state.rs | 1 + examples/ecs/system_chaining.rs | 2 +- examples/ecs/system_param.rs | 3 +- examples/ecs/system_sets.rs | 2 +- examples/ecs/timers.rs | 1 + examples/games/alien_cake_addict.rs | 2 +- examples/games/breakout.rs | 2 + examples/games/contributors.rs | 2 + examples/games/game_menu.rs | 10 +- examples/reflection/generic_reflection.rs | 4 +- examples/reflection/reflection.rs | 4 +- examples/reflection/trait_reflection.rs | 4 +- examples/scene/scene.rs | 4 +- examples/shader/animate_shader.rs | 4 +- examples/shader/array_texture.rs | 1 + .../shader/compute_shader_game_of_life.rs | 4 +- examples/shader/shader_instancing.rs | 1 + examples/stress_tests/bevymark.rs | 4 +- examples/stress_tests/many_buttons.rs | 2 + examples/stress_tests/many_foxes.rs | 2 + examples/stress_tests/many_lights.rs | 2 +- examples/stress_tests/many_sprites.rs | 1 + examples/stress_tests/transform_hierarchy.rs | 2 +- examples/tools/scene_viewer.rs | 3 +- .../transforms/global_vs_local_translation.rs | 2 +- examples/ui/font_atlas_debug.rs | 1 + examples/window/low_power.rs | 2 +- tests/how_to_test_systems.rs | 1 + tests/window/resizing.rs | 2 + 133 files changed, 804 insertions(+), 524 deletions(-) diff --git a/benches/benches/bevy_ecs/scheduling/run_criteria.rs b/benches/benches/bevy_ecs/scheduling/run_criteria.rs index 52c5fade22e53b..11a0b1efd56e86 100644 --- a/benches/benches/bevy_ecs/scheduling/run_criteria.rs +++ b/benches/benches/bevy_ecs/scheduling/run_criteria.rs @@ -1,6 +1,6 @@ use bevy_ecs::{ component::Component, - prelude::{ParallelSystemDescriptorCoercion, Res, RunCriteriaDescriptorCoercion}, + prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion}, schedule::{ShouldRun, Stage, SystemStage}, system::Query, world::World, @@ -136,7 +136,7 @@ pub fn run_criteria_no_with_labels(criterion: &mut Criterion) { group.finish(); } -#[derive(Component)] +#[derive(Component, Resource)] struct TestBool(pub bool); pub fn run_criteria_yes_with_query(criterion: &mut Criterion) { diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index ebdd2ee0df54af..7d6c027da0cdf2 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,5 +1,6 @@ use crate::{CoreStage, Plugin, PluginGroup, PluginGroupBuilder, StartupSchedule, StartupStage}; pub use bevy_derive::AppLabel; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ event::{Event, Events}, prelude::{FromWorld, IntoExclusiveSystem}, @@ -22,6 +23,11 @@ bevy_utils::define_label!( AppLabelId, ); +/// The [`Resource`] that stores the [`App`]'s [`TypeRegistry`](bevy_reflect::TypeRegistry). +#[cfg(feature = "bevy_reflect")] +#[derive(Resource, Clone, Deref, DerefMut, Default)] +pub struct AppTypeRegistry(pub bevy_reflect::TypeRegistryArc); + #[allow(clippy::needless_doctest_main)] /// A container of app logic and data. /// @@ -74,7 +80,7 @@ impl Default for App { fn default() -> Self { let mut app = App::empty(); #[cfg(feature = "bevy_reflect")] - app.init_resource::(); + app.init_resource::(); app.add_default_stages() .add_event::() @@ -647,7 +653,9 @@ impl App { /// /// ``` /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; /// # + /// #[derive(Resource)] /// struct MyCounter { /// counter: usize, /// } @@ -660,15 +668,16 @@ impl App { self } - /// Inserts a non-send [`Resource`] to the app. + /// Inserts a non-send resource to the app. /// /// You usually want to use [`insert_resource`](Self::insert_resource), - /// but there are some special cases when a [`Resource`] cannot be sent across threads. + /// but there are some special cases when a resource cannot be sent across threads. /// /// # Examples /// /// ``` /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; /// # /// struct MyCounter { /// counter: usize, @@ -694,7 +703,9 @@ impl App { /// /// ``` /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; /// # + /// #[derive(Resource)] /// struct MyCounter { /// counter: usize, /// } @@ -873,7 +884,7 @@ impl App { #[cfg(feature = "bevy_reflect")] pub fn register_type(&mut self) -> &mut Self { { - let registry = self.world.resource_mut::(); + let registry = self.world.resource_mut::(); registry.write().register::(); } self @@ -906,7 +917,7 @@ impl App { &mut self, ) -> &mut Self { { - let registry = self.world.resource_mut::(); + let registry = self.world.resource_mut::(); registry.write().register_type_data::(); } self diff --git a/crates/bevy_app/src/ci_testing.rs b/crates/bevy_app/src/ci_testing.rs index 66206d91095c35..17d8d929d6a4ce 100644 --- a/crates/bevy_app/src/ci_testing.rs +++ b/crates/bevy_app/src/ci_testing.rs @@ -1,6 +1,7 @@ use crate::{app::AppExit, App}; use serde::Deserialize; +use bevy_ecs::prelude::Resource; use bevy_utils::tracing::info; /// A configuration struct for automated CI testing. @@ -8,7 +9,7 @@ use bevy_utils::tracing::info; /// It gets used when the `bevy_ci_testing` feature is enabled to automatically /// exit a Bevy app when run through the CI. This is needed because otherwise /// Bevy apps would be stuck in the game loop and wouldn't allow the CI to progress. -#[derive(Deserialize)] +#[derive(Deserialize, Resource)] pub struct CiTestingConfig { /// The number of frames after which Bevy should exit. pub exit_after: Option, diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index c0a9c68f79c3fe..d908c420ed0ccd 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -18,6 +18,9 @@ pub use schedule_runner::*; #[allow(missing_docs)] pub mod prelude { + #[cfg(feature = "bevy_reflect")] + #[doc(hidden)] + pub use crate::AppTypeRegistry; #[doc(hidden)] pub use crate::{ app::App, CoreStage, DynamicPlugin, Plugin, PluginGroup, StartupSchedule, StartupStage, diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index aa63ce1a8b3d01..04535c045dd9d0 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -3,6 +3,7 @@ use crate::{ plugin::Plugin, }; use bevy_ecs::event::{Events, ManualEventReader}; +use bevy_ecs::prelude::Resource; use bevy_utils::{Duration, Instant}; #[cfg(target_arch = "wasm32")] @@ -34,7 +35,7 @@ impl Default for RunMode { /// The configuration information for the [`ScheduleRunnerPlugin`]. /// /// It gets added as a [`Resource`](bevy_ecs::system::Resource) inside of the [`ScheduleRunnerPlugin`]. -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, Resource)] pub struct ScheduleRunnerSettings { /// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly. pub run_mode: RunMode, diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 0d87735c969ae4..3b1e2815c61baf 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -5,7 +5,7 @@ use crate::{ RefChange, RefChangeChannel, SourceInfo, SourceMeta, }; use anyhow::Result; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_log::warn; use bevy_tasks::IoTaskPool; use bevy_utils::{Entry, HashMap, Uuid}; @@ -102,7 +102,7 @@ pub struct AssetServerInternal { /// See the [`asset_loading`] example for more information. /// /// [`asset_loading`]: https://github.com/bevyengine/bevy/tree/latest/examples/asset/asset_loading.rs -#[derive(Clone)] +#[derive(Clone, Resource)] pub struct AssetServer { pub(crate) server: Arc, } diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 9ba30143dc9644..154e20907954f6 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -5,7 +5,7 @@ use crate::{ use bevy_app::App; use bevy_ecs::{ event::{EventWriter, Events}, - system::ResMut, + system::{ResMut, Resource}, world::FromWorld, }; use bevy_utils::HashMap; @@ -66,7 +66,7 @@ impl Debug for AssetEvent { /// Remember, if there are no Strong handles for an asset (i.e. they have all been dropped), the /// asset will unload. Make sure you always have a Strong handle when you want to keep an asset /// loaded! -#[derive(Debug)] +#[derive(Debug, Resource)] pub struct Assets { assets: HashMap, events: Events>, diff --git a/crates/bevy_asset/src/debug_asset_server.rs b/crates/bevy_asset/src/debug_asset_server.rs index 6171fa18ddc90a..fafc3be7591647 100644 --- a/crates/bevy_asset/src/debug_asset_server.rs +++ b/crates/bevy_asset/src/debug_asset_server.rs @@ -6,7 +6,7 @@ use bevy_app::{App, Plugin}; use bevy_ecs::{ event::Events, schedule::SystemLabel, - system::{NonSendMut, Res, ResMut, SystemState}, + system::{NonSendMut, Res, ResMut, Resource, SystemState}, }; use bevy_tasks::{IoTaskPool, TaskPoolBuilder}; use bevy_utils::HashMap; @@ -52,6 +52,7 @@ pub struct DebugAssetServerPlugin; /// A collection that maps internal assets in a [`DebugAssetApp`]'s asset server to their mirrors in /// the main [`App`]. +#[derive(Resource)] pub struct HandleMap { /// The collection of asset handles. pub handles: HashMap, Handle>, diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 4e1dd9003495c1..4a8d37c27a24af 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -43,7 +43,10 @@ pub use loader::*; pub use path::*; use bevy_app::{prelude::Plugin, App}; -use bevy_ecs::schedule::{StageLabel, SystemStage}; +use bevy_ecs::{ + schedule::{StageLabel, SystemStage}, + system::Resource, +}; /// The names of asset stages in an [`App`] schedule. #[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)] @@ -62,6 +65,7 @@ pub enum AssetStage { pub struct AssetPlugin; /// [`AssetServer`] settings. +#[derive(Resource)] pub struct AssetServerSettings { /// The base folder where assets are loaded from, relative to the executable. pub asset_folder: String, diff --git a/crates/bevy_audio/src/audio.rs b/crates/bevy_audio/src/audio.rs index a7c3a614ba66bc..d7ac8d615795b4 100644 --- a/crates/bevy_audio/src/audio.rs +++ b/crates/bevy_audio/src/audio.rs @@ -1,9 +1,10 @@ use crate::{AudioSink, AudioSource, Decodable}; use bevy_asset::{Asset, Handle, HandleId}; +use bevy_ecs::system::Resource; use parking_lot::RwLock; use std::{collections::VecDeque, fmt}; -/// Use this resource to play audio +/// Use this [`Resource`] to play audio. /// /// ``` /// # use bevy_ecs::system::Res; @@ -13,6 +14,7 @@ use std::{collections::VecDeque, fmt}; /// audio.play(asset_server.load("my_sound.ogg")); /// } /// ``` +#[derive(Resource)] pub struct Audio where Source: Asset + Decodable, diff --git a/crates/bevy_core/src/task_pool_options.rs b/crates/bevy_core/src/task_pool_options.rs index 152489b7cf7aea..d4ab934407278d 100644 --- a/crates/bevy_core/src/task_pool_options.rs +++ b/crates/bevy_core/src/task_pool_options.rs @@ -1,3 +1,4 @@ +use bevy_ecs::prelude::Resource; use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder}; use bevy_utils::tracing::trace; @@ -33,7 +34,7 @@ impl TaskPoolThreadAssignmentPolicy { /// Helper for configuring and creating the default task pools. For end-users who want full control, /// insert the default task pools into the resource map manually. If the pools are already inserted, /// this helper will do nothing. -#[derive(Clone)] +#[derive(Clone, Resource)] pub struct DefaultTaskPoolOptions { /// If the number of physical cores is less than min_total_threads, force using /// min_total_threads diff --git a/crates/bevy_core_pipeline/src/clear_color.rs b/crates/bevy_core_pipeline/src/clear_color.rs index 59197465396de9..50861e1bebb1f8 100644 --- a/crates/bevy_core_pipeline/src/clear_color.rs +++ b/crates/bevy_core_pipeline/src/clear_color.rs @@ -13,11 +13,11 @@ pub enum ClearColorConfig { None, } -/// When used as a resource, sets the color that is used to clear the screen between frames. +/// A [`Resource`] that stores the color that is used to clear the screen between frames. /// -/// This color appears as the "background" color for simple apps, when -/// there are portions of the screen with nothing rendered. -#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)] +/// This color appears as the "background" color for simple apps, +/// when there are portions of the screen with nothing rendered. +#[derive(Resource, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)] #[reflect(Resource)] pub struct ClearColor(pub Color); diff --git a/crates/bevy_diagnostic/src/diagnostic.rs b/crates/bevy_diagnostic/src/diagnostic.rs index f53e04307e869d..4d01a485868626 100644 --- a/crates/bevy_diagnostic/src/diagnostic.rs +++ b/crates/bevy_diagnostic/src/diagnostic.rs @@ -1,3 +1,4 @@ +use bevy_ecs::system::Resource; use bevy_log::warn; use bevy_utils::{Duration, Instant, StableHashMap, Uuid}; use std::{borrow::Cow, collections::VecDeque}; @@ -154,7 +155,7 @@ impl Diagnostic { } /// A collection of [Diagnostic]s -#[derive(Debug, Default)] +#[derive(Debug, Default, Resource)] pub struct Diagnostics { // This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between // runs when all diagnostics are inserted in the same order. diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 68b73befbf2954..9075cd4284d034 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -1,12 +1,13 @@ use crate::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_time::Time; /// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count" #[derive(Default)] pub struct FrameTimeDiagnosticsPlugin; +#[derive(Resource)] pub struct FrameTimeDiagnosticsState { frame_count: u64, } diff --git a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs index 2dc7c053baf1c4..947b780725d3f2 100644 --- a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs @@ -1,6 +1,6 @@ use super::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_log::{debug, info}; use bevy_time::{Time, Timer}; use bevy_utils::Duration; @@ -13,6 +13,7 @@ pub struct LogDiagnosticsPlugin { } /// State used by the [`LogDiagnosticsPlugin`] +#[derive(Resource)] struct LogDiagnosticsState { timer: Timer, filter: Option>, diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index 7c8f8f0f300ee0..5f497161d2401e 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -91,7 +91,7 @@ Apps often require unique resources, such as asset collections, renderers, audio ```rust use bevy_ecs::prelude::*; -#[derive(Default)] +#[derive(Resource, Default)] struct Time { seconds: f32, } @@ -213,6 +213,7 @@ Resources also expose change state: ```rust use bevy_ecs::prelude::*; +#[derive(Resource)] struct Time(f32); // Prints "time changed!" if the Time resource has changed since the last run of the System diff --git a/crates/bevy_ecs/examples/change_detection.rs b/crates/bevy_ecs/examples/change_detection.rs index 8477a5164f44a7..4a72343bb65529 100644 --- a/crates/bevy_ecs/examples/change_detection.rs +++ b/crates/bevy_ecs/examples/change_detection.rs @@ -40,7 +40,7 @@ fn main() { } // This struct will be used as a Resource keeping track of the total amount of spawned entities -#[derive(Debug)] +#[derive(Debug, Resource)] struct EntityCounter { pub value: i32, } diff --git a/crates/bevy_ecs/examples/resources.rs b/crates/bevy_ecs/examples/resources.rs index c6700a6b9ed9dd..3127f52d907328 100644 --- a/crates/bevy_ecs/examples/resources.rs +++ b/crates/bevy_ecs/examples/resources.rs @@ -27,7 +27,7 @@ fn main() { } // Counter resource to be increased and read by systems -#[derive(Debug)] +#[derive(Debug, Resource)] struct Counter { pub value: i32, } diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index 440fe31468df7c..a75c4a92b3fe93 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -4,6 +4,24 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result}; +pub fn derive_resource(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_ecs_path: Path = crate::bevy_ecs_path(); + + ast.generics + .make_where_clause() + .predicates + .push(parse_quote! { Self: Send + Sync + 'static }); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause { + } + }) +} + pub fn derive_component(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); let bevy_ecs_path: Path = crate::bevy_ecs_path(); diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index bdea67ce809317..bf30ac2d30e04a 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -537,6 +537,11 @@ pub(crate) fn bevy_ecs_path() -> syn::Path { BevyManifest::default().get_path("bevy_ecs") } +#[proc_macro_derive(Resource)] +pub fn derive_resource(input: TokenStream) -> TokenStream { + component::derive_resource(input) +} + #[proc_macro_derive(Component, attributes(component))] pub fn derive_component(input: TokenStream) -> TokenStream { component::derive_component(input) diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 97882f22e1e7ae..f74ed12fa6645e 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -31,6 +31,7 @@ pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1); /// ``` /// use bevy_ecs::prelude::*; /// +/// #[derive(Resource)] /// struct MyResource(u32); /// /// fn my_system(mut resource: ResMut) { @@ -165,9 +166,9 @@ pub(crate) struct Ticks<'a> { pub(crate) change_tick: u32, } -/// Unique mutable borrow of a resource. +/// Unique mutable borrow of a [`Resource`]. /// -/// See the [`World`](crate::world::World) documentation to see the usage of a resource. +/// See the [`Resource`] documentation for usage. /// /// If you need a shared borrow, use [`Res`](crate::system::Res) instead. /// @@ -306,6 +307,8 @@ impl std::fmt::Debug for MutUntyped<'_> { #[cfg(test)] mod tests { + use bevy_ecs_macros::Resource; + use crate::{ self as bevy_ecs, change_detection::{ @@ -320,7 +323,8 @@ mod tests { #[derive(Component)] struct C; - struct R; // Resource + #[derive(Resource)] + struct R; #[test] fn change_expiration() { diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 9a5a9a41863ebf..04a7e6d74906e2 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,7 +1,7 @@ //! Event handling types. use crate as bevy_ecs; -use crate::system::{Local, Res, ResMut, SystemParam}; +use crate::system::{Local, Res, ResMut, Resource, SystemParam}; use bevy_utils::tracing::trace; use std::ops::{Deref, DerefMut}; use std::{ @@ -128,7 +128,7 @@ struct EventInstance { /// [Example usage.](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/event.rs) /// [Example usage standalone.](https://github.com/bevyengine/bevy/blob/latest/crates/bevy_ecs/examples/events.rs) /// -#[derive(Debug)] +#[derive(Debug, Resource)] pub struct Events { /// Holds the oldest still active events. /// Note that a.start_event_count + a.len() should always === events_b.start_event_count. diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index c551193e4c0d4f..7cf068c2d52c7f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -40,8 +40,8 @@ pub mod prelude { }, system::{ Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, - NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System, - SystemParamFunction, + NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, + Resource, System, SystemParamFunction, }, world::{FromWorld, Mut, World}, }; @@ -58,6 +58,7 @@ mod tests { component::{Component, ComponentId}, entity::Entity, query::{Added, ChangeTrackers, Changed, FilteredAccess, With, Without, WorldQuery}, + system::Resource, world::{Mut, World}, }; use bevy_tasks::{ComputeTaskPool, TaskPool}; @@ -69,7 +70,7 @@ mod tests { }, }; - #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] + #[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)] struct A(usize); #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)] struct B(usize); @@ -1003,16 +1004,24 @@ mod tests { #[test] fn resource() { + use crate::system::Resource; + + #[derive(Resource, PartialEq, Debug)] + struct Num(i32); + + #[derive(Resource, PartialEq, Debug)] + struct BigNum(u64); + let mut world = World::default(); - assert!(world.get_resource::().is_none()); - assert!(!world.contains_resource::()); - assert!(!world.is_resource_added::()); - assert!(!world.is_resource_changed::()); + assert!(world.get_resource::().is_none()); + assert!(!world.contains_resource::()); + assert!(!world.is_resource_added::()); + assert!(!world.is_resource_changed::()); - world.insert_resource(123); + world.insert_resource(Num(123)); let resource_id = world .components() - .get_resource_id(TypeId::of::()) + .get_resource_id(TypeId::of::()) .unwrap(); let archetype_component_id = world .archetypes() @@ -1020,61 +1029,61 @@ mod tests { .get_archetype_component_id(resource_id) .unwrap(); - assert_eq!(*world.resource::(), 123); - assert!(world.contains_resource::()); - assert!(world.is_resource_added::()); - assert!(world.is_resource_changed::()); + assert_eq!(world.resource::().0, 123); + assert!(world.contains_resource::()); + assert!(world.is_resource_added::()); + assert!(world.is_resource_changed::()); - world.insert_resource(456u64); - assert_eq!(*world.resource::(), 456u64); + world.insert_resource(BigNum(456)); + assert_eq!(world.resource::().0, 456u64); - world.insert_resource(789u64); - assert_eq!(*world.resource::(), 789); + world.insert_resource(BigNum(789)); + assert_eq!(world.resource::().0, 789); { - let mut value = world.resource_mut::(); - assert_eq!(*value, 789); - *value = 10; + let mut value = world.resource_mut::(); + assert_eq!(value.0, 789); + value.0 = 10; } assert_eq!( - world.resource::(), - &10, + world.resource::().0, + 10, "resource changes are preserved" ); assert_eq!( - world.remove_resource::(), - Some(10), + world.remove_resource::(), + Some(BigNum(10)), "removed resource has the correct value" ); assert_eq!( - world.get_resource::(), + world.get_resource::(), None, "removed resource no longer exists" ); assert_eq!( - world.remove_resource::(), + world.remove_resource::(), None, "double remove returns nothing" ); - world.insert_resource(1u64); + world.insert_resource(BigNum(1)); assert_eq!( - world.get_resource::(), - Some(&1u64), + world.get_resource::(), + Some(&BigNum(1)), "re-inserting resources works" ); assert_eq!( - world.get_resource::(), - Some(&123), + world.get_resource::(), + Some(&Num(123)), "other resources are unaffected" ); let current_resource_id = world .components() - .get_resource_id(TypeId::of::()) + .get_resource_id(TypeId::of::()) .unwrap(); assert_eq!( resource_id, current_resource_id, @@ -1120,7 +1129,7 @@ mod tests { assert_eq!( e.get::(), None, - "i32 is in the removed bundle, so it should not exist" + "Num is in the removed bundle, so it should not exist" ); assert_eq!( e.get::(), @@ -1325,12 +1334,12 @@ mod tests { #[test] fn resource_scope() { let mut world = World::default(); - world.insert_resource::(0); - world.resource_scope(|world: &mut World, mut value: Mut| { - *value += 1; - assert!(!world.contains_resource::()); + world.insert_resource(A(0)); + world.resource_scope(|world: &mut World, mut value: Mut| { + value.0 += 1; + assert!(!world.contains_resource::()); }); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); } #[test] @@ -1367,7 +1376,7 @@ mod tests { fn clear_entities() { let mut world = World::default(); - world.insert_resource::(0); + world.insert_resource(A(0)); world.spawn().insert(A(1)); world.spawn().insert(SparseStored(1)); @@ -1396,7 +1405,7 @@ mod tests { "world should not have any entities" ); assert_eq!( - *world.resource::(), + world.resource::().0, 0, "world should still contain resources" ); diff --git a/crates/bevy_ecs/src/schedule/executor_parallel.rs b/crates/bevy_ecs/src/schedule/executor_parallel.rs index 4bf36da141de00..887501a1fc87ff 100644 --- a/crates/bevy_ecs/src/schedule/executor_parallel.rs +++ b/crates/bevy_ecs/src/schedule/executor_parallel.rs @@ -11,7 +11,7 @@ use bevy_utils::tracing::Instrument; use fixedbitset::FixedBitSet; #[cfg(test)] -use SchedulingEvent::*; +use scheduling_event::*; struct SystemSchedulingMetadata { /// Used to signal the system's task to start the system. @@ -107,7 +107,7 @@ impl ParallelSystemExecutor for ParallelExecutor { #[cfg(test)] if self.events_sender.is_none() { let (sender, receiver) = async_channel::unbounded::(); - world.insert_resource(receiver); + world.insert_resource(SchedulingEvents(receiver)); self.events_sender = Some(sender); } @@ -260,7 +260,7 @@ impl ParallelExecutor { } #[cfg(test)] if started_systems != 0 { - self.emit_event(StartedSystems(started_systems)); + self.emit_event(SchedulingEvent::StartedSystems(started_systems)); } // Remove now running systems from the queue. self.queued.difference_with(&self.running); @@ -308,29 +308,43 @@ impl ParallelExecutor { } #[cfg(test)] -#[derive(Debug, PartialEq, Eq)] -enum SchedulingEvent { - StartedSystems(usize), +mod scheduling_event { + use crate as bevy_ecs; + use crate::system::Resource; + use async_channel::Receiver; + + #[derive(Debug, PartialEq, Eq)] + pub(super) enum SchedulingEvent { + StartedSystems(usize), + } + + #[derive(Resource)] + pub(super) struct SchedulingEvents(pub(crate) Receiver); } +#[cfg(test)] #[cfg(test)] mod tests { - use super::SchedulingEvent::{self, *}; use crate::{ - schedule::{SingleThreadedExecutor, Stage, SystemStage}, - system::{NonSend, Query, Res, ResMut}, + self as bevy_ecs, + component::Component, + schedule::{ + executor_parallel::scheduling_event::*, SingleThreadedExecutor, Stage, SystemStage, + }, + system::{NonSend, Query, Res, ResMut, Resource}, world::World, }; - use async_channel::Receiver; - use crate as bevy_ecs; - use crate::component::Component; + use SchedulingEvent::StartedSystems; + #[derive(Component)] struct W(T); + #[derive(Resource, Default)] + struct Counter(usize); fn receive_events(world: &World) -> Vec { let mut events = Vec::new(); - while let Ok(event) = world.resource::>().try_recv() { + while let Ok(event) = world.resource::().0.try_recv() { events.push(event); } events @@ -355,9 +369,9 @@ mod tests { #[test] fn resources() { let mut world = World::new(); - world.insert_resource(0usize); - fn wants_mut(_: ResMut) {} - fn wants_ref(_: Res) {} + world.init_resource::(); + fn wants_mut(_: ResMut) {} + fn wants_ref(_: Res) {} let mut stage = SystemStage::parallel() .with_system(wants_mut) .with_system(wants_mut); diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index a14a5db7be32a7..72e7e399338306 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -1,4 +1,5 @@ use crate::{ + self as bevy_ecs, change_detection::CHECK_TICK_THRESHOLD, component::ComponentId, prelude::IntoSystem, @@ -12,6 +13,7 @@ use crate::{ }, world::{World, WorldId}, }; +use bevy_ecs_macros::Resource; use bevy_utils::{ tracing::{info, warn}, HashMap, HashSet, @@ -50,7 +52,7 @@ impl_downcast!(Stage); /// /// The checker may report a system more times than the amount of constraints it would actually need /// to have unambiguous order with regards to a group of already-constrained systems. -#[derive(Default)] +#[derive(Resource, Default)] pub struct ReportExecutionOrderAmbiguities; /// Stores and executes systems. Execution order is not defined unless explicitly specified; @@ -982,15 +984,22 @@ mod tests { use crate as bevy_ecs; use crate::component::Component; + use crate::system::Resource; + #[derive(Component)] struct W(T); + #[derive(Resource)] + struct R(usize); + + #[derive(Resource, Default)] + struct EntityCount(Vec); fn make_exclusive(tag: usize) -> impl FnMut(&mut World) { - move |world| world.resource_mut::>().push(tag) + move |world| world.resource_mut::().0.push(tag) } - fn make_parallel(tag: usize) -> impl FnMut(ResMut>) { - move |mut resource: ResMut>| resource.push(tag) + fn make_parallel(tag: usize) -> impl FnMut(ResMut) { + move |mut resource: ResMut| resource.0.push(tag) } fn every_other_time(mut has_ran: Local) -> ShouldRun { @@ -1005,48 +1014,48 @@ mod tests { #[test] fn insertion_points() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().at_start()) .with_system(make_parallel(1)) .with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(3).exclusive_system().at_end()); stage.run(&mut world); - assert_eq!(*world.resource_mut::>(), vec![0, 1, 2, 3]); + assert_eq!(world.resource_mut::().0, vec![0, 1, 2, 3]); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 0, 1, 2, 3] ); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(3).exclusive_system().at_end()) .with_system(make_parallel(1)) .with_system(make_exclusive(0).exclusive_system().at_start()); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 3]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 3]); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 0, 1, 2, 3] ); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_parallel(2).exclusive_system().before_commands()) .with_system(make_parallel(3).exclusive_system().at_end()) .with_system(make_parallel(1)) .with_system(make_parallel(0).exclusive_system().at_start()); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 3]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 3]); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 0, 1, 2, 3] ); } @@ -1054,7 +1063,7 @@ mod tests { #[test] fn exclusive_after() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(1).exclusive_system().label("1").after("0")) .with_system(make_exclusive(2).exclusive_system().after("1")) @@ -1062,13 +1071,13 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); } #[test] fn exclusive_before() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(1).exclusive_system().label("1").before("2")) .with_system(make_exclusive(2).exclusive_system().label("2")) @@ -1076,13 +1085,13 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); } #[test] fn exclusive_mixed() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().label("2")) .with_system(make_exclusive(1).exclusive_system().after("0").before("2")) @@ -1093,7 +1102,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1101,7 +1110,7 @@ mod tests { #[test] fn exclusive_multiple_labels() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system( make_exclusive(1) @@ -1119,9 +1128,9 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().after("01").label("2")) .with_system(make_exclusive(1).exclusive_system().label("01").after("0")) @@ -1132,11 +1141,11 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().label("234").label("2")) .with_system( @@ -1158,7 +1167,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1166,7 +1175,7 @@ mod tests { #[test] fn exclusive_redundant_constraints() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system( make_exclusive(2) @@ -1197,7 +1206,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1205,7 +1214,7 @@ mod tests { #[test] fn exclusive_mixed_across_sets() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().label("2")) .with_system_set( @@ -1219,7 +1228,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1227,7 +1236,7 @@ mod tests { #[test] fn exclusive_run_criteria() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().before("1")) .with_system_set( @@ -1242,7 +1251,7 @@ mod tests { stage.run(&mut world); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 0, 2, 0, 1, 2, 0, 2] ); } @@ -1251,7 +1260,7 @@ mod tests { #[should_panic] fn exclusive_cycle_1() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().label("0").after("0")); stage.run(&mut world); @@ -1261,7 +1270,7 @@ mod tests { #[should_panic] fn exclusive_cycle_2() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().label("0").after("1")) .with_system(make_exclusive(1).exclusive_system().label("1").after("0")); @@ -1272,7 +1281,7 @@ mod tests { #[should_panic] fn exclusive_cycle_3() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().label("0")) .with_system(make_exclusive(1).exclusive_system().after("0").before("2")) @@ -1283,7 +1292,7 @@ mod tests { #[test] fn parallel_after() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(1).after("0").label("1")) .with_system(make_parallel(2).after("1")) @@ -1291,13 +1300,13 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); } #[test] fn parallel_before() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(1).label("1").before("2")) .with_system(make_parallel(2).label("2")) @@ -1305,13 +1314,13 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); } #[test] fn parallel_mixed() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(2).label("2")) .with_system(make_parallel(1).after("0").before("2")) @@ -1322,7 +1331,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1330,7 +1339,7 @@ mod tests { #[test] fn parallel_multiple_labels() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(1).label("first").after("0")) .with_system(make_parallel(2).after("first")) @@ -1338,9 +1347,9 @@ mod tests { stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 2, 0, 1, 2]); + assert_eq!(world.resource::().0, vec![0, 1, 2, 0, 1, 2]); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_parallel(2).after("01").label("2")) .with_system(make_parallel(1).label("01").after("0")) @@ -1351,11 +1360,11 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_parallel(2).label("234").label("2")) .with_system(make_parallel(1).before("234").after("0")) @@ -1366,7 +1375,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1374,7 +1383,7 @@ mod tests { #[test] fn parallel_redundant_constraints() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system( make_parallel(2) @@ -1400,7 +1409,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1408,7 +1417,7 @@ mod tests { #[test] fn parallel_mixed_across_sets() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(2).label("2")) .with_system_set( @@ -1422,7 +1431,7 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4] ); } @@ -1431,7 +1440,7 @@ mod tests { fn parallel_run_criteria() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system( make_parallel(0) @@ -1444,9 +1453,9 @@ mod tests { stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1, 1, 0, 1, 1]); + assert_eq!(world.resource::().0, vec![0, 1, 1, 0, 1, 1]); - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_parallel(0).before("1")) .with_system_set( @@ -1461,12 +1470,12 @@ mod tests { stage.run(&mut world); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 0, 2, 0, 1, 2, 0, 2] ); // Reusing criteria. - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system_run_criteria(every_other_time.label("every other time")) .with_system(make_parallel(0).before("1")) @@ -1488,13 +1497,13 @@ mod tests { stage.run(&mut world); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 0, 3, 0, 1, 2, 3, 0, 3] ); assert_eq!(stage.run_criteria.len(), 1); // Piping criteria. - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); fn eot_piped(input: In, has_ran: Local) -> ShouldRun { if let ShouldRun::Yes | ShouldRun::YesAndCheckAgain = input.0 { every_other_time(has_ran) @@ -1529,13 +1538,13 @@ mod tests { stage.run(&mut world); } assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 4, 0, 0, 1, 0, 0, 1, 2, 3, 4, 0, 0, 1, 0, 0, 1, 2, 3, 4] ); assert_eq!(stage.run_criteria.len(), 3); // Discarding extra criteria with matching labels. - world.resource_mut::>().clear(); + world.resource_mut::().0.clear(); let mut stage = SystemStage::parallel() .with_system(make_parallel(0).before("1")) @@ -1552,7 +1561,7 @@ mod tests { stage.run(&mut world); stage.run(&mut world); assert_eq!( - *world.resource::>(), + world.resource::().0, vec![0, 1, 2, 3, 0, 3, 0, 1, 2, 3, 0, 3] ); assert_eq!(stage.run_criteria.len(), 1); @@ -1572,7 +1581,7 @@ mod tests { #[should_panic] fn parallel_cycle_1() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel().with_system(make_parallel(0).label("0").after("0")); stage.run(&mut world); } @@ -1581,7 +1590,7 @@ mod tests { #[should_panic] fn parallel_cycle_2() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(0).label("0").after("1")) .with_system(make_parallel(1).label("1").after("0")); @@ -1593,7 +1602,7 @@ mod tests { fn parallel_cycle_3() { let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(make_parallel(0).label("0")) .with_system(make_parallel(1).after("0").before("2")) @@ -1628,7 +1637,7 @@ mod tests { } fn empty() {} - fn resource(_: ResMut) {} + fn resource(_: ResMut) {} fn component(_: Query<&mut W>) {} let mut world = World::new(); @@ -1998,47 +2007,41 @@ mod tests { #[test] fn archetype_update_single_executor() { - fn query_count_system( - mut entity_count: ResMut, - query: Query, - ) { - *entity_count = query.iter().count(); + fn query_count_system(mut entity_count: ResMut, query: Query) { + *entity_count = R(query.iter().count()); } let mut world = World::new(); - world.insert_resource(0_usize); + world.insert_resource(R(0)); let mut stage = SystemStage::single(query_count_system); let entity = world.spawn().insert_bundle(()).id(); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); world.get_entity_mut(entity).unwrap().insert(W(1)); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); } #[test] fn archetype_update_parallel_executor() { - fn query_count_system( - mut entity_count: ResMut, - query: Query, - ) { - *entity_count = query.iter().count(); + fn query_count_system(mut entity_count: ResMut, query: Query) { + *entity_count = R(query.iter().count()); } let mut world = World::new(); - world.insert_resource(0_usize); + world.insert_resource(R(0)); let mut stage = SystemStage::parallel(); stage.add_system(query_count_system); let entity = world.spawn().insert_bundle(()).id(); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); world.get_entity_mut(entity).unwrap().insert(W(1)); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); } #[test] @@ -2060,12 +2063,12 @@ mod tests { commands.spawn().insert(Foo); } - fn count_entities(query: Query<&Foo>, mut res: ResMut>) { - res.push(query.iter().len()); + fn count_entities(query: Query<&Foo>, mut res: ResMut) { + res.0.push(query.iter().len()); } let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(spawn_entity.label("spawn")) .with_system_set( @@ -2077,7 +2080,7 @@ mod tests { stage.run(&mut world); stage.run(&mut world); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 2]); + assert_eq!(world.resource::().0, vec![0, 2]); } #[test] @@ -2099,12 +2102,12 @@ mod tests { commands.spawn().insert(Foo); } - fn count_entities(query: Query<&Foo>, mut res: ResMut>) { - res.push(query.iter().len()); + fn count_entities(query: Query<&Foo>, mut res: ResMut) { + res.0.push(query.iter().len()); } let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity); let mut stage_count = SystemStage::parallel() .with_run_criteria(even_number_of_entities_critiera) @@ -2117,6 +2120,6 @@ mod tests { stage_spawn.run(&mut world); stage_count.run(&mut world); stage_spawn.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 2]); + assert_eq!(world.resource::().0, vec![0, 2]); } } diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 96e4409da98984..c08dfaa886e520 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -3,13 +3,15 @@ use crate::{ RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun, SystemSet, }, - system::{In, IntoChainSystem, Local, Res, ResMut}, + system::{In, IntoChainSystem, Local, Res, ResMut, Resource}, }; use std::{ any::TypeId, fmt::{self, Debug}, hash::Hash, }; +// Required for derive macros +use crate as bevy_ecs; pub trait StateData: Send + Sync + Clone + Eq + Debug + Hash + 'static {} impl StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'static {} @@ -21,7 +23,7 @@ impl StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'stat /// * Pop removes the current state, and unpauses the last paused state /// * Set replaces the active state with a new one /// * Replace unwinds the state stack, and replaces the entire stack with a single new state -#[derive(Debug)] +#[derive(Debug, Resource)] pub struct State { transition: Option>, /// The current states in the stack. @@ -489,9 +491,12 @@ mod test { #[test] fn state_test() { + #[derive(Resource, Default)] + struct NameList(Vec<&'static str>); + let mut world = World::default(); - world.insert_resource(Vec::<&'static str>::new()); + world.init_resource::(); world.insert_resource(State::new(MyState::S1)); let mut stage = SystemStage::parallel(); @@ -500,55 +505,55 @@ mod test { stage .add_system_set( State::on_enter_set(MyState::S1) - .with_system(|mut r: ResMut>| r.push("startup")), + .with_system(|mut r: ResMut| r.0.push("startup")), ) .add_system_set(State::on_update_set(MyState::S1).with_system( - |mut r: ResMut>, mut s: ResMut>| { - r.push("update S1"); + |mut r: ResMut, mut s: ResMut>| { + r.0.push("update S1"); s.overwrite_replace(MyState::S2).unwrap(); }, )) .add_system_set( State::on_enter_set(MyState::S2) - .with_system(|mut r: ResMut>| r.push("enter S2")), + .with_system(|mut r: ResMut| r.0.push("enter S2")), ) .add_system_set(State::on_update_set(MyState::S2).with_system( - |mut r: ResMut>, mut s: ResMut>| { - r.push("update S2"); + |mut r: ResMut, mut s: ResMut>| { + r.0.push("update S2"); s.overwrite_replace(MyState::S3).unwrap(); }, )) .add_system_set( State::on_exit_set(MyState::S2) - .with_system(|mut r: ResMut>| r.push("exit S2")), + .with_system(|mut r: ResMut| r.0.push("exit S2")), ) .add_system_set( State::on_enter_set(MyState::S3) - .with_system(|mut r: ResMut>| r.push("enter S3")), + .with_system(|mut r: ResMut| r.0.push("enter S3")), ) .add_system_set(State::on_update_set(MyState::S3).with_system( - |mut r: ResMut>, mut s: ResMut>| { - r.push("update S3"); + |mut r: ResMut, mut s: ResMut>| { + r.0.push("update S3"); s.overwrite_push(MyState::S4).unwrap(); }, )) .add_system_set( State::on_pause_set(MyState::S3) - .with_system(|mut r: ResMut>| r.push("pause S3")), + .with_system(|mut r: ResMut| r.0.push("pause S3")), ) .add_system_set(State::on_update_set(MyState::S4).with_system( - |mut r: ResMut>, mut s: ResMut>| { - r.push("update S4"); + |mut r: ResMut, mut s: ResMut>| { + r.0.push("update S4"); s.overwrite_push(MyState::S5).unwrap(); }, )) .add_system_set(State::on_inactive_update_set(MyState::S4).with_system( - (|mut r: ResMut>| r.push("inactive S4")).label("inactive s4"), + (|mut r: ResMut| r.0.push("inactive S4")).label("inactive s4"), )) .add_system_set( State::on_update_set(MyState::S5).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S5"); + (|mut r: ResMut, mut s: ResMut>| { + r.0.push("update S5"); s.overwrite_push(MyState::S6).unwrap(); }) .after("inactive s4"), @@ -556,15 +561,15 @@ mod test { ) .add_system_set( State::on_inactive_update_set(MyState::S5).with_system( - (|mut r: ResMut>| r.push("inactive S5")) + (|mut r: ResMut| r.0.push("inactive S5")) .label("inactive s5") .after("inactive s4"), ), ) .add_system_set( State::on_update_set(MyState::S6).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S6"); + (|mut r: ResMut, mut s: ResMut>| { + r.0.push("update S6"); s.overwrite_push(MyState::Final).unwrap(); }) .after("inactive s5"), @@ -572,11 +577,11 @@ mod test { ) .add_system_set( State::on_resume_set(MyState::S4) - .with_system(|mut r: ResMut>| r.push("resume S4")), + .with_system(|mut r: ResMut| r.0.push("resume S4")), ) .add_system_set( State::on_exit_set(MyState::S5) - .with_system(|mut r: ResMut>| r.push("exit S4")), + .with_system(|mut r: ResMut| r.0.push("exit S4")), ); const EXPECTED: &[&str] = &[ @@ -606,9 +611,9 @@ mod test { ]; stage.run(&mut world); - let mut collected = world.resource_mut::>(); + let mut collected = world.resource_mut::(); let mut count = 0; - for (found, expected) in collected.drain(..).zip(EXPECTED) { + for (found, expected) in collected.0.drain(..).zip(EXPECTED) { assert_eq!(found, *expected); count += 1; } @@ -627,26 +632,32 @@ mod test { Main, } - fn should_run_once(mut flag: ResMut, test_name: Res<&'static str>) { - assert!(!*flag, "{:?}", *test_name); - *flag = true; + #[derive(Resource)] + struct Flag(bool); + + #[derive(Resource)] + struct Name(&'static str); + + fn should_run_once(mut flag: ResMut, test_name: Res) { + assert!(!flag.0, "{:?}", test_name.0); + flag.0 = true; } let mut world = World::new(); world.insert_resource(State::new(AppState::Main)); - world.insert_resource(false); - world.insert_resource("control"); + world.insert_resource(Flag(false)); + world.insert_resource(Name("control")); let mut stage = SystemStage::parallel().with_system(should_run_once); stage.run(&mut world); - assert!(*world.resource::(), "after control"); + assert!(world.resource::().0, "after control"); - world.insert_resource(false); - world.insert_resource("test"); + world.insert_resource(Flag(false)); + world.insert_resource(Name("test")); let mut stage = SystemStage::parallel() .with_system_set(State::::get_driver()) .with_system(should_run_once); stage.run(&mut world); - assert!(*world.resource::(), "after test"); + assert!(world.resource::().0, "after test"); } #[test] @@ -664,8 +675,11 @@ mod test { EnterFinish, } + #[derive(Resource, Default)] + struct LoadStatusStack(Vec); + let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); world.insert_resource(State::new(LoadState::Load)); let mut stage = SystemStage::parallel(); @@ -675,15 +689,16 @@ mod test { stage .add_system_set( State::on_enter_set(LoadState::Load) - .with_system(|mut r: ResMut>| r.push(LoadStatus::EnterLoad)), + .with_system(|mut r: ResMut| r.0.push(LoadStatus::EnterLoad)), ) .add_system_set( State::on_exit_set(LoadState::Load) - .with_system(|mut r: ResMut>| r.push(LoadStatus::ExitLoad)), + .with_system(|mut r: ResMut| r.0.push(LoadStatus::ExitLoad)), ) .add_system_set( - State::on_enter_set(LoadState::Finish) - .with_system(|mut r: ResMut>| r.push(LoadStatus::EnterFinish)), + State::on_enter_set(LoadState::Finish).with_system( + |mut r: ResMut| r.0.push(LoadStatus::EnterFinish), + ), ); stage.run(&mut world); @@ -720,9 +735,9 @@ mod test { LoadStatus::EnterFinish, ]; - let mut collected = world.resource_mut::>(); + let mut collected = world.resource_mut::(); let mut count = 0; - for (found, expected) in collected.drain(..).zip(EXPECTED) { + for (found, expected) in collected.0.drain(..).zip(EXPECTED) { assert_eq!(found, *expected); count += 1; } diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 1521eebe529da8..19c057bc4b664b 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -24,7 +24,7 @@ use super::Resource; /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::system::Command; /// // Our world resource -/// #[derive(Default)] +/// #[derive(Resource, Default)] /// struct Counter(u64); /// /// // Our custom command @@ -331,7 +331,7 @@ impl<'w, 's> Commands<'w, 's> { /// ``` /// # use bevy_ecs::prelude::*; /// # - /// # #[derive(Default)] + /// # #[derive(Resource, Default)] /// # struct Scoreboard { /// # current_score: u32, /// # high_score: u32, @@ -359,6 +359,7 @@ impl<'w, 's> Commands<'w, 's> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct Scoreboard { /// # current_score: u32, /// # high_score: u32, @@ -385,6 +386,7 @@ impl<'w, 's> Commands<'w, 's> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct Scoreboard { /// # current_score: u32, /// # high_score: u32, @@ -410,7 +412,7 @@ impl<'w, 's> Commands<'w, 's> { /// /// ``` /// # use bevy_ecs::{system::Command, prelude::*}; - /// #[derive(Default)] + /// #[derive(Resource, Default)] /// struct Counter(u64); /// /// struct AddToCounter(u64); @@ -472,6 +474,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct PlayerEntity { entity: Entity } /// # #[derive(Component)] /// # struct Health(u32); @@ -548,6 +551,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct PlayerEntity { entity: Entity } /// # /// # #[derive(Component)] @@ -580,6 +584,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct TargetEnemy { entity: Entity } /// # #[derive(Component)] /// # struct Enemy; @@ -609,6 +614,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct CharacterToRemove { entity: Entity } /// # /// fn remove_character_system( @@ -854,7 +860,7 @@ mod tests { use crate::{ self as bevy_ecs, component::Component, - system::{CommandQueue, Commands}, + system::{CommandQueue, Commands, Resource}, world::World, }; use std::sync::{ @@ -881,7 +887,7 @@ mod tests { } } - #[derive(Component)] + #[derive(Component, Resource)] struct W(T); fn simple_command(world: &mut World) { @@ -992,21 +998,21 @@ mod tests { let mut queue = CommandQueue::default(); { let mut commands = Commands::new(&mut queue, &world); - commands.insert_resource(123); - commands.insert_resource(456.0); + commands.insert_resource(W(123i32)); + commands.insert_resource(W(456.0f64)); } queue.apply(&mut world); - assert!(world.contains_resource::()); - assert!(world.contains_resource::()); + assert!(world.contains_resource::>()); + assert!(world.contains_resource::>()); { let mut commands = Commands::new(&mut queue, &world); // test resource removal - commands.remove_resource::(); + commands.remove_resource::>(); } queue.apply(&mut world); - assert!(!world.contains_resource::()); - assert!(world.contains_resource::()); + assert!(!world.contains_resource::>()); + assert!(world.contains_resource::>()); } } diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index f70f2bedfbc01f..8005b1a701f110 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -112,7 +112,7 @@ mod tests { entity::Entity, query::With, schedule::{Stage, SystemStage}, - system::{Commands, IntoExclusiveSystem, Query, ResMut}, + system::{Commands, IntoExclusiveSystem, Query, ResMut, Resource}, world::World, }; @@ -123,49 +123,55 @@ mod tests { fn parallel_with_commands_as_exclusive() { let mut world = World::new(); + #[derive(Resource)] + struct Counter(usize); + fn removal( mut commands: Commands, query: Query>, - mut counter: ResMut, + mut counter: ResMut, ) { for entity in &query { - *counter += 1; + counter.0 += 1; commands.entity(entity).remove::(); } } let mut stage = SystemStage::parallel().with_system(removal); world.spawn().insert(Foo(0.0f32)); - world.insert_resource(0usize); + world.insert_resource(Counter(0)); stage.run(&mut world); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); let mut stage = SystemStage::parallel().with_system(removal.exclusive_system()); world.spawn().insert(Foo(0.0f32)); - world.insert_resource(0usize); + world.insert_resource(Counter(0)); stage.run(&mut world); stage.run(&mut world); - assert_eq!(*world.resource::(), 1); + assert_eq!(world.resource::().0, 1); } #[test] fn update_archetype_for_exclusive_system_coerced() { + #[derive(Resource, Default)] + struct CountEntities(Vec); + fn spawn_entity(mut commands: crate::prelude::Commands) { commands.spawn().insert(Foo(0.0)); } - fn count_entities(query: Query<&Foo>, mut res: ResMut>) { - res.push(query.iter().len()); + fn count_entities(query: Query<&Foo>, mut res: ResMut) { + res.0.push(query.iter().len()); } let mut world = World::new(); - world.insert_resource(Vec::::new()); + world.init_resource::(); let mut stage = SystemStage::parallel() .with_system(spawn_entity) .with_system(count_entities.exclusive_system()); stage.run(&mut world); stage.run(&mut world); - assert_eq!(*world.resource::>(), vec![0, 1]); + assert_eq!(world.resource::().0, vec![0, 1]); } } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index a1e209e2426d8e..8ca47e0f4c3115 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -82,6 +82,7 @@ impl SystemMeta { /// use bevy_ecs::event::Events; /// /// struct MyEvent; +/// #[derive(Resource)] /// struct MyResource(u32); /// /// #[derive(Component)] @@ -110,6 +111,7 @@ impl SystemMeta { /// use bevy_ecs::event::Events; /// /// struct MyEvent; +/// #[derive(Resource)] /// struct CachedSystemState<'w, 's>{ /// event_state: SystemState> /// } @@ -246,10 +248,9 @@ impl FromWorld for SystemState { /// # Examples /// /// ``` -/// use bevy_ecs::system::IntoSystem; -/// use bevy_ecs::system::Res; +/// use bevy_ecs::prelude::*; /// -/// fn my_system_function(an_usize_resource: Res) {} +/// fn my_system_function(a_usize_local: Local) {} /// /// let system = IntoSystem::into_system(my_system_function); /// ``` @@ -526,6 +527,7 @@ impl Copy for SystemTypeIdLabel {} /// assert_eq!(chained_system.run((), &mut world), Some(42)); /// } /// +/// #[derive(Resource)] /// struct Message(String); /// /// fn parse_message(message: Res) -> Result { diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 7b368b62ef471d..ec3fd6ea785536 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -17,6 +17,7 @@ //! # struct Player { alive: bool } //! # #[derive(Component)] //! # struct Score(u32); +//! # #[derive(Resource)] //! # struct Round(u32); //! # //! fn update_score_system( @@ -139,22 +140,28 @@ mod tests { schedule::{Schedule, Stage, SystemStage}, system::{ Commands, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, - RemovedComponents, Res, ResMut, System, SystemState, + RemovedComponents, Res, ResMut, Resource, System, SystemState, }, world::{FromWorld, World}, }; - #[derive(Component, Debug, Eq, PartialEq, Default)] + #[derive(Resource, PartialEq, Debug)] + enum SystemRan { + Yes, + No, + } + + #[derive(Component, Resource, Debug, Eq, PartialEq, Default)] struct A; - #[derive(Component)] + #[derive(Component, Resource)] struct B; - #[derive(Component)] + #[derive(Component, Resource)] struct C; - #[derive(Component)] + #[derive(Component, Resource)] struct D; - #[derive(Component)] + #[derive(Component, Resource)] struct E; - #[derive(Component)] + #[derive(Component, Resource)] struct F; #[derive(Component)] @@ -187,7 +194,7 @@ mod tests { #[test] fn query_system_gets() { fn query_system( - mut ran: ResMut, + mut ran: ResMut, entity_query: Query>, b_query: Query<&B>, a_c_query: Query<(&A, &C)>, @@ -227,11 +234,11 @@ mod tests { "entity 3 should have D" ); - *ran = true; + *ran = SystemRan::Yes; } let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(SystemRan::No); world.spawn().insert_bundle((A,)); world.spawn().insert_bundle((A, B)); world.spawn().insert_bundle((A, C)); @@ -239,14 +246,14 @@ mod tests { run_system(&mut world, query_system); - assert!(*world.resource::(), "system ran"); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] fn or_param_set_system() { // Regression test for issue #762 fn query_system( - mut ran: ResMut, + mut ran: ResMut, mut set: ParamSet<( Query<(), Or<(Changed, Changed)>>, Query<(), Or<(Added, Added)>>, @@ -258,24 +265,33 @@ mod tests { assert_eq!(changed, 1); assert_eq!(added, 1); - *ran = true; + *ran = SystemRan::Yes; } let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(SystemRan::No); world.spawn().insert_bundle((A, B)); run_system(&mut world, query_system); - assert!(*world.resource::(), "system ran"); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] fn changed_resource_system() { + use crate::system::Resource; + + #[derive(Resource)] + struct Flipper(bool); + + #[derive(Resource)] struct Added(usize); + + #[derive(Resource)] struct Changed(usize); + fn incr_e_on_flip( - value: Res, + value: Res, mut changed: ResMut, mut added: ResMut, ) { @@ -289,7 +305,7 @@ mod tests { } let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(Flipper(false)); world.insert_resource(Added(0)); world.insert_resource(Changed(0)); @@ -310,7 +326,7 @@ mod tests { assert_eq!(world.resource::().0, 1); assert_eq!(world.resource::().0, 1); - *world.resource_mut::() = true; + world.resource_mut::().0 = true; schedule.run(&mut world); assert_eq!(world.resource::().0, 1); assert_eq!(world.resource::().0, 2); @@ -434,7 +450,7 @@ mod tests { run_system(&mut world, sys); } - #[derive(Default)] + #[derive(Default, Resource)] struct BufferRes { _buffer: Vec, } @@ -477,36 +493,42 @@ mod tests { #[test] fn local_system() { let mut world = World::default(); - world.insert_resource(1u32); - world.insert_resource(false); + world.insert_resource(ProtoFoo { value: 1 }); + world.insert_resource(SystemRan::No); + struct Foo { value: u32, } + #[derive(Resource)] + struct ProtoFoo { + value: u32, + } + impl FromWorld for Foo { fn from_world(world: &mut World) -> Self { Foo { - value: *world.resource::() + 1, + value: world.resource::().value + 1, } } } - fn sys(local: Local, mut modified: ResMut) { + fn sys(local: Local, mut system_ran: ResMut) { assert_eq!(local.value, 2); - *modified = true; + *system_ran = SystemRan::Yes; } run_system(&mut world, sys); // ensure the system actually ran - assert!(*world.resource::()); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] fn non_send_option_system() { let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(SystemRan::No); struct NotSend1(std::rc::Rc); struct NotSend2(std::rc::Rc); world.insert_non_send_resource(NotSend1(std::rc::Rc::new(0))); @@ -514,34 +536,38 @@ mod tests { fn sys( op: Option>, mut _op2: Option>, - mut run: ResMut, + mut system_ran: ResMut, ) { op.expect("NonSend should exist"); - *run = true; + *system_ran = SystemRan::Yes; } run_system(&mut world, sys); // ensure the system actually ran - assert!(*world.resource::()); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] fn non_send_system() { let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(SystemRan::No); struct NotSend1(std::rc::Rc); struct NotSend2(std::rc::Rc); world.insert_non_send_resource(NotSend1(std::rc::Rc::new(1))); world.insert_non_send_resource(NotSend2(std::rc::Rc::new(2))); - fn sys(_op: NonSend, mut _op2: NonSendMut, mut run: ResMut) { - *run = true; + fn sys( + _op: NonSend, + mut _op2: NonSendMut, + mut system_ran: ResMut, + ) { + *system_ran = SystemRan::Yes; } run_system(&mut world, sys); - assert!(*world.resource::()); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] @@ -553,13 +579,16 @@ mod tests { let spurious_entity = world.spawn().id(); // Track which entities we want to operate on + #[derive(Resource)] struct Despawned(Entity); world.insert_resource(Despawned(entity_to_despawn)); + + #[derive(Resource)] struct Removed(Entity); world.insert_resource(Removed(entity_to_remove_w_from)); // Verify that all the systems actually ran - #[derive(Default)] + #[derive(Default, Resource)] struct NSystems(usize); world.insert_resource(NSystems::default()); @@ -615,7 +644,7 @@ mod tests { #[test] fn world_collections_system() { let mut world = World::default(); - world.insert_resource(false); + world.insert_resource(SystemRan::No); world.spawn().insert_bundle((W(42), W(true))); fn sys( archetypes: &Archetypes, @@ -623,7 +652,7 @@ mod tests { entities: &Entities, bundles: &Bundles, query: Query>>, - mut modified: ResMut, + mut system_ran: ResMut, ) { assert_eq!(query.iter().count(), 1, "entity exists"); for entity in &query { @@ -647,13 +676,13 @@ mod tests { "entity's bundle components exactly match entity's archetype components" ); } - *modified = true; + *system_ran = SystemRan::Yes; } run_system(&mut world, sys); // ensure the system actually ran - assert!(*world.resource::()); + assert_eq!(*world.resource::(), SystemRan::Yes); } #[test] @@ -751,7 +780,7 @@ mod tests { #[test] fn read_system_state() { - #[derive(Eq, PartialEq, Debug)] + #[derive(Eq, PartialEq, Debug, Resource)] struct A(usize); #[derive(Component, Eq, PartialEq, Debug)] @@ -774,7 +803,7 @@ mod tests { #[test] fn write_system_state() { - #[derive(Eq, PartialEq, Debug)] + #[derive(Resource, Eq, PartialEq, Debug)] struct A(usize); #[derive(Component, Eq, PartialEq, Debug)] diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 262d12436a5aa3..d1bf9b2b747c68 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -735,6 +735,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct SelectedCharacter { entity: Entity } /// # #[derive(Component)] /// # struct Character { name: String } @@ -841,6 +842,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct PoisonedCharacter { character_id: Entity } /// # #[derive(Component)] /// # struct Health(u32); @@ -967,6 +969,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct SelectedCharacter { entity: Entity } /// # #[derive(Component)] /// # struct Character { name: String } @@ -1022,6 +1025,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct PoisonedCharacter { character_id: Entity } /// # #[derive(Component)] /// # struct Health(u32); @@ -1233,7 +1237,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// # /// # #[derive(Component)] /// # struct Player; - /// # #[derive(Component)] + /// # #[derive(Resource)] /// # struct Score(u32); /// fn update_score_system(query: Query<(), With>, mut score: ResMut) { /// if !query.is_empty() { @@ -1258,6 +1262,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// # #[derive(Component)] /// # struct InRange; /// # + /// # #[derive(Resource)] /// # struct Target { /// # entity: Entity, /// # } @@ -1352,6 +1357,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> { /// ``` /// # use bevy_ecs::prelude::*; /// # + /// # #[derive(Resource)] /// # struct SelectedCharacter { entity: Entity } /// # #[derive(Component)] /// # struct Character { name: String } diff --git a/crates/bevy_ecs/src/system/system_chaining.rs b/crates/bevy_ecs/src/system/system_chaining.rs index cf8e27b5ad757f..0fac40e2313897 100644 --- a/crates/bevy_ecs/src/system/system_chaining.rs +++ b/crates/bevy_ecs/src/system/system_chaining.rs @@ -34,6 +34,7 @@ use std::borrow::Cow; /// assert_eq!(chained_system.run((), &mut world), Some(42)); /// } /// +/// #[derive(Resource)] /// struct Message(String); /// /// fn parse_message_system(message: Res) -> Result { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0472e45c367c66..4935cf4634e971 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -11,6 +11,7 @@ use crate::{ system::{CommandQueue, Commands, Query, SystemMeta}, world::{FromWorld, World}, }; +pub use bevy_ecs_macros::Resource; pub use bevy_ecs_macros::SystemParam; use bevy_ecs_macros::{all_tuples, impl_param_set}; use bevy_ptr::UnsafeCellDeref; @@ -48,14 +49,16 @@ use std::{ /// /// ``` /// # use bevy_ecs::prelude::*; +/// # #[derive(Resource)] +/// # struct SomeResource; /// use std::marker::PhantomData; /// use bevy_ecs::system::SystemParam; /// /// #[derive(SystemParam)] /// struct MyParam<'w, 's> { -/// foo: Res<'w, usize>, +/// foo: Res<'w, SomeResource>, /// #[system_param(ignore)] -/// marker: PhantomData<&'s usize>, +/// marker: PhantomData<&'s ()>, /// } /// /// fn my_system(param: MyParam) { @@ -217,13 +220,42 @@ pub struct ParamSetState SystemParamFetch<'w, 's>>(T); impl_param_set!(); +/// A type that can be inserted into a [`World`] as a singleton. +/// +/// You can access resource data in systems using the [`Res`] and [`ResMut`] system parameters +/// +/// Only one resource of each type can be stored in a [`World`] at any given time. +/// +/// # Examples +/// +/// ``` +/// # let mut world = World::default(); +/// # let mut schedule = Schedule::default(); +/// # schedule.add_stage("update", SystemStage::parallel()); +/// # use bevy_ecs::prelude::*; +/// #[derive(Resource)] +/// struct MyResource { value: u32 } +/// +/// world.insert_resource(MyResource { value: 42 }); +/// +/// fn read_resource_system(resource: Res) { +/// assert_eq!(resource.value, 42); +/// } +/// +/// fn write_resource_system(mut resource: ResMut) { +/// assert_eq!(resource.value, 42); +/// resource.value = 0; +/// assert_eq!(resource.value, 0); +/// } +/// # schedule.add_system_to_stage("update", read_resource_system.label("first")); +/// # schedule.add_system_to_stage("update", write_resource_system.after("first")); +/// # schedule.run_once(&mut world); +/// ``` pub trait Resource: Send + Sync + 'static {} -impl Resource for T where T: Send + Sync + 'static {} - -/// Shared borrow of a resource. +/// Shared borrow of a [`Resource`]. /// -/// See the [`World`] documentation to see the usage of a resource. +/// See the [`Resource`] documentation for usage. /// /// If you need a unique mutable borrow, use [`ResMut`] instead. /// @@ -632,6 +664,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState { /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::system::assert_is_system; /// struct Config(u32); +/// #[derive(Resource)] /// struct Myu32Wrapper(u32); /// fn reset_to_system(value: Config) -> impl FnMut(ResMut) { /// move |mut val| val.0 = value.0 @@ -640,12 +673,12 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState { /// // .add_system(reset_to_system(my_config)) /// # assert_is_system(reset_to_system(Config(10))); /// ``` -pub struct Local<'a, T: Resource + FromWorld>(&'a mut T); +pub struct Local<'a, T: FromWorld + Send + Sync + 'static>(&'a mut T); -// SAFETY: Local only accesses internal state -unsafe impl ReadOnlySystemParamFetch for LocalState {} +// SAFE: Local only accesses internal state +unsafe impl ReadOnlySystemParamFetch for LocalState {} -impl<'a, T: Resource + FromWorld> Debug for Local<'a, T> +impl<'a, T: FromWorld + Send + Sync + 'static> Debug for Local<'a, T> where T: Debug, { @@ -654,7 +687,7 @@ where } } -impl<'a, T: Resource + FromWorld> Deref for Local<'a, T> { +impl<'a, T: FromWorld + Send + Sync + 'static> Deref for Local<'a, T> { type Target = T; #[inline] @@ -663,7 +696,7 @@ impl<'a, T: Resource + FromWorld> Deref for Local<'a, T> { } } -impl<'a, T: Resource + FromWorld> DerefMut for Local<'a, T> { +impl<'a, T: FromWorld + Send + Sync + 'static> DerefMut for Local<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.0 @@ -672,20 +705,20 @@ impl<'a, T: Resource + FromWorld> DerefMut for Local<'a, T> { /// The [`SystemParamState`] of [`Local`]. #[doc(hidden)] -pub struct LocalState(T); +pub struct LocalState(T); -impl<'a, T: Resource + FromWorld> SystemParam for Local<'a, T> { +impl<'a, T: Send + Sync + 'static + FromWorld> SystemParam for Local<'a, T> { type Fetch = LocalState; } -// SAFETY: only local state is accessed -unsafe impl SystemParamState for LocalState { +// SAFE: only local state is accessed +unsafe impl SystemParamState for LocalState { fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self { Self(T::from_world(world)) } } -impl<'w, 's, T: Resource + FromWorld> SystemParamFetch<'w, 's> for LocalState { +impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for LocalState { type Item = Local<'s, T>; #[inline] diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d849d4a39914a5..6d0fd231fc81fb 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -30,6 +30,7 @@ use std::{ mod identifier; pub use identifier::WorldId; + /// Stores and exposes operations on [entities](Entity), [components](Component), resources, /// and their associated metadata. /// @@ -43,41 +44,12 @@ pub use identifier::WorldId; /// To mutate different parts of the world simultaneously, /// use [`World::resource_scope`] or [`SystemState`](crate::system::SystemState). /// -/// # Resources -/// -/// Worlds can also store *resources*, which are unique instances of a given type that don't -/// belong to a specific Entity. There are also *non send resources*, which can only be -/// accessed on the main thread. -/// -/// ## Usage of global resources -/// -/// 1. Insert the resource into the `World`, using [`World::insert_resource`]. -/// 2. Fetch the resource from a system, using [`Res`](crate::system::Res) or [`ResMut`](crate::system::ResMut). -/// -/// ``` -/// # let mut world = World::default(); -/// # let mut schedule = Schedule::default(); -/// # schedule.add_stage("update", SystemStage::parallel()); -/// # use bevy_ecs::prelude::*; -/// # -/// struct MyResource { value: u32 } +/// ## Resources /// -/// world.insert_resource(MyResource { value: 42 }); -/// -/// fn read_resource_system(resource: Res) { -/// assert_eq!(resource.value, 42); -/// } -/// -/// fn write_resource_system(mut resource: ResMut) { -/// assert_eq!(resource.value, 42); -/// resource.value = 0; -/// assert_eq!(resource.value, 0); -/// } -/// # -/// # schedule.add_system_to_stage("update", read_resource_system.label("first")); -/// # schedule.add_system_to_stage("update", write_resource_system.after("first")); -/// # schedule.run_once(&mut world); -/// ``` +/// Worlds can also store [`Resource`]s, +/// which are unique instances of a given type that don't belong to a specific Entity. +/// There are also *non send resources*, which can only be accessed on the main thread. +/// See [`Resource`] for usage. pub struct World { id: WorldId, pub(crate) entities: Entities, @@ -1089,8 +1061,8 @@ impl World { /// /// # Example /// ``` - /// use bevy_ecs::{component::Component, world::{World, Mut}}; - /// #[derive(Component)] + /// use bevy_ecs::prelude::*; + /// #[derive(Resource)] /// struct A(u32); /// #[derive(Component)] /// struct B(u32); @@ -1597,6 +1569,7 @@ mod tests { change_detection::DetectChanges, component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType}, ptr::OwningPtr, + system::Resource, }; use bevy_ecs_macros::Component; use bevy_utils::HashSet; @@ -1726,7 +1699,7 @@ mod tests { ); } - #[derive(Component)] + #[derive(Resource)] struct TestResource(u32); #[test] diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index 08571bb6c4e51d..9db47dfd8c0a2f 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -318,40 +318,47 @@ impl<'w> WorldCell<'w> { #[cfg(test)] mod tests { use super::BASE_ACCESS; - use crate::{archetype::ArchetypeId, world::World}; + use crate as bevy_ecs; + use crate::{archetype::ArchetypeId, system::Resource, world::World}; use std::any::TypeId; + #[derive(Resource)] + struct A(u32); + + #[derive(Resource)] + struct B(u64); + #[test] fn world_cell() { let mut world = World::default(); - world.insert_resource(1u32); - world.insert_resource(1u64); + world.insert_resource(A(1)); + world.insert_resource(B(1)); let cell = world.cell(); { - let mut a = cell.resource_mut::(); - assert_eq!(1, *a); - *a = 2; + let mut a = cell.resource_mut::(); + assert_eq!(1, a.0); + a.0 = 2; } { - let a = cell.resource::(); - assert_eq!(2, *a, "ensure access is dropped"); + let a = cell.resource::(); + assert_eq!(2, a.0, "ensure access is dropped"); - let b = cell.resource::(); + let a2 = cell.resource::(); assert_eq!( - 2, *b, + 2, a2.0, "ensure multiple immutable accesses can occur at the same time" ); } { - let a = cell.resource_mut::(); + let a = cell.resource_mut::(); assert_eq!( - 2, *a, + 2, a.0, "ensure both immutable accesses are dropped, enabling a new mutable access" ); - let b = cell.resource::(); + let b = cell.resource::(); assert_eq!( - 1, *b, + 1, b.0, "ensure multiple non-conflicting mutable accesses can occur at the same time" ); } @@ -360,23 +367,20 @@ mod tests { #[test] fn world_access_reused() { let mut world = World::default(); - world.insert_resource(1u32); + world.insert_resource(A(1)); { let cell = world.cell(); { - let mut a = cell.resource_mut::(); - assert_eq!(1, *a); - *a = 2; + let mut a = cell.resource_mut::(); + assert_eq!(1, a.0); + a.0 = 2; } } - let u32_component_id = world - .components - .get_resource_id(TypeId::of::()) - .unwrap(); + let resource_id = world.components.get_resource_id(TypeId::of::()).unwrap(); let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap(); let u32_archetype_component_id = resource_archetype - .get_archetype_component_id(u32_component_id) + .get_archetype_component_id(resource_id) .unwrap(); assert_eq!(world.archetype_component_access.access.len(), 1); assert_eq!( @@ -393,38 +397,38 @@ mod tests { #[should_panic] fn world_cell_double_mut() { let mut world = World::default(); - world.insert_resource(1u32); + world.insert_resource(A(1)); let cell = world.cell(); - let _value_a = cell.resource_mut::(); - let _value_b = cell.resource_mut::(); + let _value_a = cell.resource_mut::(); + let _value_b = cell.resource_mut::(); } #[test] #[should_panic] fn world_cell_ref_and_mut() { let mut world = World::default(); - world.insert_resource(1u32); + world.insert_resource(A(1)); let cell = world.cell(); - let _value_a = cell.resource::(); - let _value_b = cell.resource_mut::(); + let _value_a = cell.resource::(); + let _value_b = cell.resource_mut::(); } #[test] #[should_panic] fn world_cell_mut_and_ref() { let mut world = World::default(); - world.insert_resource(1u32); + world.insert_resource(A(1)); let cell = world.cell(); - let _value_a = cell.resource_mut::(); - let _value_b = cell.resource::(); + let _value_a = cell.resource_mut::(); + let _value_b = cell.resource::(); } #[test] fn world_cell_ref_and_ref() { let mut world = World::default(); - world.insert_resource(1u32); + world.insert_resource(A(1)); let cell = world.cell(); - let _value_a = cell.resource::(); - let _value_b = cell.resource::(); + let _value_a = cell.resource::(); + let _value_b = cell.resource::(); } } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.rs index 988e267de5e2f4..d7f2cfa92c6945 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_iter_many_mut_iterator_safety.rs @@ -3,8 +3,8 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn system(mut query: Query<&mut A>, e: Res) { - let iter = query.iter_many_mut([*e]); +fn system(mut query: Query<&mut A>, e: Entity) { + let iter = query.iter_many_mut([e]); // This should fail to compile. is_iterator(iter) diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs index cc778bff7bff1a..559d97ed8bc9be 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs @@ -3,9 +3,9 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn system(mut query: Query<&mut A>, e: Res) { - let a1 = query.get_mut(*e).unwrap(); - let a2 = query.get_mut(*e).unwrap(); +fn system(mut query: Query<&mut A>, e: Entity) { + let a1 = query.get_mut(e).unwrap(); + let a2 = query.get_mut(e).unwrap(); // this should fail to compile println!("{} {}", a1.0, a2.0); } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr index aeec0f6ce54826..2adc856eb30f60 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr @@ -1,10 +1,10 @@ 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 -8 | let a2 = query.get_mut(*e).unwrap(); - | ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here +7 | let a1 = query.get_mut(e).unwrap(); + | ---------------- first mutable borrow occurs here +8 | let a2 = query.get_mut(e).unwrap(); + | ^^^^^^^^^^^^^^^^ second mutable borrow occurs here 9 | // this should fail to compile 10 | println!("{} {}", a1.0, a2.0); | -- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.rs index 0f3893e1003783..94ce5129975fde 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.rs @@ -3,9 +3,9 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn system(mut query: Query<&mut A>, e: Res) { - let a1 = query.get_many([*e, *e]).unwrap(); - let a2 = query.get_mut(*e).unwrap(); +fn system(mut query: Query<&mut A>, e: Entity) { + let a1 = query.get_many([e, e]).unwrap(); + let a2 = query.get_mut(e).unwrap(); // this should fail to compile println!("{} {}", a1[0].0, a2.0); } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.stderr index 2227a52dcfab2f..d4f9b4d7cba1b5 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_lifetime_safety.stderr @@ -1,10 +1,10 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable --> tests/ui/system_query_get_many_lifetime_safety.rs:8:14 | -7 | let a1 = query.get_many([*e, *e]).unwrap(); - | ------------------------ immutable borrow occurs here -8 | let a2 = query.get_mut(*e).unwrap(); - | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here +7 | let a1 = query.get_many([e, e]).unwrap(); + | ---------------------- immutable borrow occurs here +8 | let a2 = query.get_mut(e).unwrap(); + | ^^^^^^^^^^^^^^^^ mutable borrow occurs here 9 | // this should fail to compile 10 | println!("{} {}", a1[0].0, a2.0); | ----- immutable borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.rs index 27782d940b8fb2..7aad2055ebaf62 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.rs @@ -3,9 +3,9 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn system(mut query: Query<&mut A>, e: Res) { - let a1 = query.get_many_mut([*e, *e]).unwrap(); - let a2 = query.get_mut(*e).unwrap(); +fn system(mut query: Query<&mut A>, e: Entity) { + let a1 = query.get_many_mut([e, e]).unwrap(); + let a2 = query.get_mut(e).unwrap(); // this should fail to compile println!("{} {}", a1[0].0, a2.0); } diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.stderr index b3fcdb2a1d9968..72cd25f3ed0c9a 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_many_mut_lifetime_safety.stderr @@ -1,10 +1,10 @@ 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 -8 | let a2 = query.get_mut(*e).unwrap(); - | ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here +7 | let a1 = query.get_many_mut([e, e]).unwrap(); + | -------------------------- first mutable borrow occurs here +8 | let a2 = query.get_mut(e).unwrap(); + | ^^^^^^^^^^^^^^^^ second mutable borrow occurs here 9 | // this should fail to compile 10 | println!("{} {}", a1[0].0, a2.0); | ----- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_many_mut_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_many_mut_lifetime_safety.rs index 7a1cb107fb40ca..2344a68a63cf92 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_many_mut_lifetime_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_many_mut_lifetime_safety.rs @@ -3,9 +3,9 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn system(mut query: Query<&mut A>, e: Res) { +fn system(mut query: Query<&mut A>, e: Entity) { let mut results = Vec::new(); - let mut iter = query.iter_many_mut([*e, *e]); + let mut iter = query.iter_many_mut([e, e]); while let Some(a) = iter.fetch_next() { // this should fail to compile results.push(a); diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs index b9e161382027c9..492b4dc0fc9c37 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs @@ -3,23 +3,23 @@ use bevy_ecs::prelude::*; #[derive(Component)] struct A(usize); -fn query_set(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Res) { +fn query_set(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Entity) { let mut q2 = queries.p0(); - let mut b = q2.get_mut(*e).unwrap(); + let mut b = q2.get_mut(e).unwrap(); let q1 = queries.p1(); - let a = q1.get(*e).unwrap(); + let a = q1.get(e).unwrap(); // this should fail to compile b.0 = a.0 } -fn query_set_flip(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Res) { +fn query_set_flip(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Entity) { let q1 = queries.p1(); - let a = q1.get(*e).unwrap(); + let a = q1.get(e).unwrap(); let mut q2 = queries.p0(); - let mut b = q2.get_mut(*e).unwrap(); + let mut b = q2.get_mut(e).unwrap(); // this should fail to compile b.0 = a.0 diff --git a/crates/bevy_input/src/axis.rs b/crates/bevy_input/src/axis.rs index 5ba80f3ee9c923..228365f16bdffa 100644 --- a/crates/bevy_input/src/axis.rs +++ b/crates/bevy_input/src/axis.rs @@ -1,10 +1,11 @@ +use bevy_ecs::system::Resource; use bevy_utils::HashMap; use std::hash::Hash; /// Stores the position data of the input devices of type `T`. /// /// The values are stored as `f32`s, which range from [`Axis::MIN`] to [`Axis::MAX`], inclusive. -#[derive(Debug)] +#[derive(Debug, Resource)] pub struct Axis { /// The position data of the input devices. axis_data: HashMap, diff --git a/crates/bevy_input/src/gamepad.rs b/crates/bevy_input/src/gamepad.rs index 4dbc00dcadd974..8effb31e740ff0 100644 --- a/crates/bevy_input/src/gamepad.rs +++ b/crates/bevy_input/src/gamepad.rs @@ -1,6 +1,6 @@ use crate::{Axis, Input}; use bevy_ecs::event::{EventReader, EventWriter}; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_utils::{tracing::info, HashMap, HashSet}; /// A gamepad with an associated `ID`. @@ -39,7 +39,7 @@ impl Gamepad { /// The [`Gamepad`]s are registered and deregistered in the [`gamepad_connection_system`] /// whenever a [`GamepadEventType::Connected`] or [`GamepadEventType::Disconnected`] /// event is received. -#[derive(Default, Debug)] +#[derive(Resource, Default, Debug)] pub struct Gamepads { /// The collection of the connected [`Gamepad`]s. gamepads: HashSet, @@ -144,18 +144,19 @@ impl GamepadEvent { /// # use bevy_input::gamepad::GamepadEventRaw; /// # use bevy_app::prelude::*; /// # use bevy_ecs::prelude::*; -/// # -/// // This system sets the `bool` resource to `true` if the `South` button -/// // of the first gamepad is pressed. +/// #[derive(Resource)] +/// struct MyResource(bool); +/// +/// // This system sets the bool inside `MyResource` to `true` if the `South` button of the first gamepad is pressed. /// fn change_resource_on_gamepad_button_press( -/// mut my_resource: ResMut, +/// mut my_resource: ResMut, /// gamepads: Res, /// button_inputs: ResMut>, /// ) { /// let gamepad = gamepads.iter().next().unwrap(); /// let gamepad_button= GamepadButton::new(*gamepad, GamepadButtonType::South); /// -/// *my_resource = button_inputs.pressed(gamepad_button); +/// my_resource.0 = button_inputs.pressed(gamepad_button); /// } /// /// // Create our app. @@ -163,7 +164,7 @@ impl GamepadEvent { /// /// // Add the input plugin and the system/resource we want to test. /// app.add_plugin(InputPlugin) -/// .insert_resource(false) +/// .insert_resource(MyResource(false)) /// .add_system(change_resource_on_gamepad_button_press); /// /// // Define our dummy gamepad input data. @@ -185,8 +186,8 @@ impl GamepadEvent { /// app.update(); /// /// // At this point you can check if your game logic corresponded correctly to the gamepad input. -/// // In this example we are checking if the `bool` resource was updated from `false` to `true`. -/// assert!(app.world.resource::()); +/// // In this example we are checking if the bool in `MyResource` was updated from `false` to `true`. +/// assert!(app.world.resource::().0); /// /// // Send the gamepad input event to mark the `South` gamepad button as released. /// // This updates the `Input` resource accordingly. @@ -198,8 +199,8 @@ impl GamepadEvent { /// // Advance the execution of the schedule by another cycle. /// app.update(); /// -/// // Check if the `bool` resource was updated from `true` to `false`. -/// assert!(!app.world.resource::()); +/// // Check if the bool in `MyResource` was updated from `true` to `false`. +/// assert!(!app.world.resource::().0); /// # /// # bevy_ecs::system::assert_is_system(change_resource_on_gamepad_button_press); /// ``` @@ -391,7 +392,7 @@ impl GamepadAxis { /// /// The [`GamepadSettings`] are used inside of the [`gamepad_event_system`], but are never written to /// inside of `bevy`. To modify these settings, mutate the corresponding resource. -#[derive(Default, Debug)] +#[derive(Resource, Default, Debug)] pub struct GamepadSettings { /// The default button settings. pub default_button_settings: ButtonSettings, diff --git a/crates/bevy_input/src/input.rs b/crates/bevy_input/src/input.rs index 86f102dcb5b0bb..7d7f91812a62a6 100644 --- a/crates/bevy_input/src/input.rs +++ b/crates/bevy_input/src/input.rs @@ -1,3 +1,4 @@ +use bevy_ecs::system::Resource; use bevy_utils::HashSet; use std::hash::Hash; @@ -32,7 +33,7 @@ use bevy_ecs::schedule::State; /// * Call the [`Input::press`] method for each press event. /// * Call the [`Input::release`] method for each release event. /// * Call the [`Input::clear`] method at each frame start, before processing events. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Resource)] pub struct Input { /// A collection of every button that is currently being pressed. pressed: HashSet, diff --git a/crates/bevy_input/src/touch.rs b/crates/bevy_input/src/touch.rs index cd0b59db7cabcc..e7565b594adb5b 100644 --- a/crates/bevy_input/src/touch.rs +++ b/crates/bevy_input/src/touch.rs @@ -1,5 +1,5 @@ use bevy_ecs::event::EventReader; -use bevy_ecs::system::ResMut; +use bevy_ecs::system::{ResMut, Resource}; use bevy_math::Vec2; use bevy_utils::HashMap; @@ -201,7 +201,7 @@ impl From<&TouchInput> for Touch { /// ## Updating /// /// The resource is updated inside of the [`touch_screen_input_system`](crate::touch::touch_screen_input_system). -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Resource)] pub struct Touches { /// A collection of every [`Touch`] that is currently being pressed. pressed: HashMap, diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index ec1ad205fb56b3..75b6ea7510e336 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -14,6 +14,7 @@ trace = [ "tracing-error" ] [dependencies] bevy_app = { path = "../bevy_app", version = "0.9.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } +bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" } tracing-subscriber = {version = "0.3.1", features = ["registry", "env-filter"]} tracing-chrome = { version = "0.4.0", optional = true } diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 2985133721a087..a7518327a56786 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -30,6 +30,8 @@ pub use bevy_utils::tracing::{ Level, }; +use bevy_ecs::prelude::Resource; + use bevy_app::{App, Plugin}; use tracing_log::LogTracer; #[cfg(feature = "tracing-chrome")] @@ -89,6 +91,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter}; pub struct LogPlugin; /// `LogPlugin` settings +#[derive(Resource)] pub struct LogSettings { /// Filters logs using the [`EnvFilter`] format pub filter: String, diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 30df637eec32be..f5db34d9838656 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -23,6 +23,7 @@ bevy_render = { path = "../bevy_render", version = "0.9.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } bevy_window = { path = "../bevy_window", version = "0.9.0-dev" } +bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" } # other bitflags = "1.2" diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 09349ce5baed4b..87d2a2ecb4991a 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -73,7 +73,7 @@ impl PointLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug, Reflect)] +#[derive(Resource, Clone, Debug, Reflect)] #[reflect(Resource)] pub struct PointLightShadowMap { pub size: usize, @@ -208,7 +208,7 @@ impl DirectionalLight { pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6; } -#[derive(Clone, Debug, Reflect)] +#[derive(Resource, Clone, Debug, Reflect)] #[reflect(Resource)] pub struct DirectionalLightShadowMap { pub size: usize, @@ -224,7 +224,7 @@ impl Default for DirectionalLightShadowMap { } /// An ambient light, which lights the entire scene equally. -#[derive(Clone, Debug, ExtractResource, Reflect)] +#[derive(Resource, Clone, Debug, ExtractResource, Reflect)] #[reflect(Resource)] pub struct AmbientLight { pub color: Color, @@ -773,7 +773,7 @@ impl PointLightAssignmentData { } } -#[derive(Default)] +#[derive(Resource, Default)] pub struct GlobalVisiblePointLights { entities: HashSet, } diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index bb5d07db453743..dddb27a887749b 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -5,6 +5,7 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; use bevy_core_pipeline::core_3d::{AlphaMask3d, Opaque3d, Transparent3d}; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ entity::Entity, event::EventReader, @@ -12,7 +13,7 @@ use bevy_ecs::{ schedule::ParallelSystemDescriptorCoercion, system::{ lifetimeless::{Read, SQuery, SRes}, - Commands, Local, Query, Res, ResMut, SystemParamItem, + Commands, Local, Query, Res, ResMut, Resource, SystemParamItem, }, world::FromWorld, }; @@ -224,6 +225,7 @@ where } /// Render pipeline data for a given [`Material`]. +#[derive(Resource)] pub struct MaterialPipeline { pub mesh_pipeline: MeshPipeline, pub material_layout: BindGroupLayout, @@ -434,6 +436,7 @@ pub struct PreparedMaterial { pub properties: MaterialProperties, } +#[derive(Resource)] struct ExtractedMaterials { extracted: Vec<(Handle, M)>, removed: Vec>, @@ -449,7 +452,14 @@ impl Default for ExtractedMaterials { } /// Stores all prepared representations of [`Material`] assets for as long as they exist. -pub type RenderMaterials = HashMap, PreparedMaterial>; +#[derive(Resource, Deref, DerefMut)] +pub struct RenderMaterials(pub HashMap, PreparedMaterial>); + +impl Default for RenderMaterials { + fn default() -> Self { + Self(Default::default()) + } +} /// This system extracts all created or modified assets of the corresponding [`Material`] type /// into the "render world". diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 5a9e75b0ea0e0e..c9c13d69bf310b 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -214,6 +214,7 @@ pub const MAX_UNIFORM_BUFFER_POINT_LIGHTS: usize = 256; pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float; +#[derive(Resource)] pub struct ShadowPipeline { pub view_layout: BindGroupLayout, pub mesh_layout: BindGroupLayout, @@ -662,6 +663,7 @@ pub struct ViewLightsUniformOffset { // at least that many are supported using this constant and SupportedBindingType::from_device() pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3; +#[derive(Resource)] pub struct GlobalLightMeta { pub gpu_point_lights: GpuPointLights, pub entity_to_index: HashMap, @@ -686,7 +688,7 @@ impl GlobalLightMeta { } } -#[derive(Default)] +#[derive(Resource, Default)] pub struct LightMeta { pub view_gpu_lights: DynamicUniformBuffer, pub shadow_view_bind_group: Option, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 0bb456bcaa5fa0..082e20b7335851 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -165,7 +165,7 @@ pub fn extract_meshes( commands.insert_or_spawn_batch(not_caster_commands); } -#[derive(Debug, Default)] +#[derive(Resource, Debug, Default)] pub struct ExtractedJoints { pub buffer: Vec, } @@ -247,7 +247,7 @@ pub fn extract_skinned_meshes( commands.insert_or_spawn_batch(values); } -#[derive(Clone)] +#[derive(Resource, Clone)] pub struct MeshPipeline { pub view_layout: BindGroupLayout, pub mesh_layout: BindGroupLayout, @@ -651,6 +651,7 @@ impl SpecializedMeshPipeline for MeshPipeline { } } +#[derive(Resource)] pub struct MeshBindGroup { pub normal: BindGroup, pub skinned: Option, @@ -706,6 +707,7 @@ pub fn queue_mesh_bind_group( // ignoring the rest, whether they're valid for other dynamic offsets or not. This trick may // be supported later in encase, and then we should make use of it. +#[derive(Resource)] pub struct SkinnedMeshUniform { pub buffer: BufferVec, } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index b194fa5983e54e..6d08adb3025ad8 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -62,13 +62,14 @@ fn extract_wireframes(mut commands: Commands, query: Extract, diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 25a9384aa7c422..f57c07b6a2c004 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -69,6 +69,7 @@ impl Plugin for UniformComponentP } /// Stores all uniforms of the component type. +#[derive(Resource)] pub struct ComponentUniforms { uniforms: DynamicUniformBuffer, } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 1b57127113ee9f..5efbb5ba9c03ab 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -42,7 +42,7 @@ use crate::{ primitives::{CubemapFrusta, Frustum}, render_graph::RenderGraph, render_resource::{PipelineCache, Shader, ShaderLoader}, - renderer::render_system, + renderer::{render_system, RenderInstance}, texture::ImagePlugin, view::{ViewPlugin, WindowRenderPlugin}, }; @@ -91,9 +91,13 @@ pub enum RenderStage { /// This resource is only available during [`RenderStage::Extract`] and not /// during command application of that stage. /// See [`Extract`] for more details. -#[derive(Default)] +#[derive(Resource, Default)] pub struct MainWorld(World); +/// The Render App World. This is only available as a resource during the Extract step. +#[derive(Resource, Default)] +pub struct RenderWorld(World); + impl Deref for MainWorld { type Target = World; @@ -194,7 +198,7 @@ impl Plugin for RenderPlugin { ) .add_stage(RenderStage::Cleanup, SystemStage::parallel()) .init_resource::() - .insert_resource(instance) + .insert_resource(RenderInstance(instance)) .insert_resource(device) .insert_resource(queue) .insert_resource(adapter_info) @@ -323,7 +327,7 @@ impl Plugin for RenderPlugin { /// A "scratch" world used to avoid allocating new worlds every frame when /// swapping out the [`MainWorld`] for [`RenderStage::Extract`]. -#[derive(Default)] +#[derive(Resource, Default)] struct ScratchMainWorld(World); /// Executes the [`Extract`](RenderStage::Extract) stage of the renderer. diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index d0d0b2d97af96e..b4c38dae3c9445 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -1,6 +1,7 @@ use crate::{Extract, RenderApp, RenderStage}; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetEvent, Assets, Handle}; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ prelude::*, system::{StaticSystemParam, SystemParam, SystemParamItem}, @@ -101,6 +102,7 @@ impl Plugin for RenderAssetPlugin { } /// Temporarily stores the extracted and removed assets of the current frame. +#[derive(Resource)] pub struct ExtractedAssets { extracted: Vec<(Handle, A::ExtractedAsset)>, removed: Vec>, @@ -117,7 +119,14 @@ impl Default for ExtractedAssets { /// Stores all GPU representations ([`RenderAsset::PreparedAssets`](RenderAsset::PreparedAsset)) /// of [`RenderAssets`](RenderAsset) as long as they exist. -pub type RenderAssets = HashMap, ::PreparedAsset>; +#[derive(Resource, Deref, DerefMut)] +pub struct RenderAssets(HashMap, A::PreparedAsset>); + +impl Default for RenderAssets { + fn default() -> Self { + Self(Default::default()) + } +} /// This system extracts all crated or modified assets of the corresponding [`RenderAsset`] type /// into the "render world". @@ -155,6 +164,7 @@ fn extract_render_asset( // TODO: consider storing inside system? /// All assets that should be prepared next frame. +#[derive(Resource)] pub struct PrepareNextFrameAssets { assets: Vec<(Handle, A::ExtractedAsset)>, } diff --git a/crates/bevy_render/src/render_graph/graph.rs b/crates/bevy_render/src/render_graph/graph.rs index c93e513cfcafd8..88b19dce1f4b40 100644 --- a/crates/bevy_render/src/render_graph/graph.rs +++ b/crates/bevy_render/src/render_graph/graph.rs @@ -5,7 +5,7 @@ use crate::{ }, renderer::RenderContext, }; -use bevy_ecs::prelude::World; +use bevy_ecs::{prelude::World, system::Resource}; use bevy_utils::HashMap; use std::{borrow::Cow, fmt::Debug}; @@ -48,7 +48,7 @@ use super::EdgeExistence; /// graph.add_node("output_node", MyNode); /// graph.add_node_edge("output_node", "input_node").unwrap(); /// ``` -#[derive(Default)] +#[derive(Resource, Default)] pub struct RenderGraph { nodes: HashMap, node_names: HashMap, NodeId>, diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 5e344d452b10b4..76725aec816027 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -7,7 +7,8 @@ use bevy_ecs::{ all_tuples, entity::Entity, system::{ - lifetimeless::SRes, ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState, + lifetimeless::SRes, ReadOnlySystemParamFetch, Resource, SystemParam, SystemParamItem, + SystemState, }, world::World, }; @@ -100,6 +101,7 @@ impl DrawFunctionsInternal

{ /// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock. /// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used. +#[derive(Resource)] pub struct DrawFunctions { internal: RwLock>, } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 341e3b21646ef9..905c754bb4e703 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -10,8 +10,8 @@ use crate::{ Extract, }; use bevy_asset::{AssetEvent, Assets, Handle}; -use bevy_ecs::event::EventReader; use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::{event::EventReader, system::Resource}; use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet}; use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref, sync::Arc}; use thiserror::Error; @@ -263,6 +263,7 @@ impl LayoutCache { } } +#[derive(Resource)] pub struct PipelineCache { layout_cache: LayoutCache, shader_cache: ShaderCache, diff --git a/crates/bevy_render/src/render_resource/pipeline_specializer.rs b/crates/bevy_render/src/render_resource/pipeline_specializer.rs index ee73bf1cf6bff9..c8faa95a7ca58c 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -6,6 +6,7 @@ use crate::{ VertexBufferLayout, }, }; +use bevy_ecs::system::Resource; use bevy_utils::{ default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap, PreHashMapExt, @@ -18,6 +19,7 @@ pub trait SpecializedRenderPipeline { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; } +#[derive(Resource)] pub struct SpecializedRenderPipelines { cache: HashMap, } @@ -80,6 +82,7 @@ pub trait SpecializedMeshPipeline { ) -> Result; } +#[derive(Resource)] pub struct SpecializedMeshPipelines { mesh_layout_cache: PreHashMap>, diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index ecc49125969bbb..5b175a14644065 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -1,6 +1,7 @@ mod graph_runner; mod render_device; +use bevy_derive::{Deref, DerefMut}; use bevy_utils::tracing::{error, info, info_span}; pub use graph_runner::*; pub use render_device::*; @@ -28,7 +29,7 @@ pub fn render_system(world: &mut World) { if let Err(e) = RenderGraphRunner::run( graph, render_device.clone(), // TODO: is this clone really necessary? - render_queue, + &render_queue.0, world, ) { error!("Error running render graph:"); @@ -84,11 +85,17 @@ pub fn render_system(world: &mut World) { } /// This queue is used to enqueue tasks for the GPU to execute asynchronously. -pub type RenderQueue = Arc; +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct RenderQueue(pub Arc); /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], /// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). -pub type RenderInstance = Instance; +#[derive(Resource, Deref, DerefMut)] +pub struct RenderInstance(pub Instance); + +/// The `AdapterInfo` of the adapter in use by the renderer. +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct RenderAdapterInfo(pub AdapterInfo); /// Initializes the renderer by retrieving and preparing the GPU instance, device and queue /// for the specified backend. @@ -96,7 +103,7 @@ pub async fn initialize_renderer( instance: &Instance, options: &WgpuSettings, request_adapter_options: &RequestAdapterOptions<'_>, -) -> (RenderDevice, RenderQueue, AdapterInfo) { +) -> (RenderDevice, RenderQueue, RenderAdapterInfo) { let adapter = instance .request_adapter(request_adapter_options) .await @@ -245,7 +252,11 @@ pub async fn initialize_renderer( .unwrap(); let device = Arc::new(device); let queue = Arc::new(queue); - (RenderDevice::from(device), queue, adapter_info) + ( + RenderDevice::from(device), + RenderQueue(queue), + RenderAdapterInfo(adapter_info), + ) } /// The context with all information required to interact with the GPU. diff --git a/crates/bevy_render/src/renderer/render_device.rs b/crates/bevy_render/src/renderer/render_device.rs index e15ed803f777f6..0ac5acbc6fa84c 100644 --- a/crates/bevy_render/src/renderer/render_device.rs +++ b/crates/bevy_render/src/renderer/render_device.rs @@ -2,13 +2,14 @@ use crate::render_resource::{ BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor, RenderPipeline, Sampler, Texture, }; +use bevy_ecs::system::Resource; use std::sync::Arc; use wgpu::{util::DeviceExt, BufferAsyncError, BufferBindingType}; use super::RenderQueue; /// This GPU device is responsible for the creation of most rendering and compute resources. -#[derive(Clone)] +#[derive(Resource, Clone)] pub struct RenderDevice { device: Arc, } diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index b65c878cee8d67..52c15fad7720bd 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use bevy_ecs::system::Resource; pub use wgpu::{Backends, Features as WgpuFeatures, Limits as WgpuLimits, PowerPreference}; /// Configures the priority used when automatically configuring the features/limits of `wgpu`. @@ -22,7 +23,7 @@ pub enum WgpuSettingsPriority { /// NOTE: If you want to use [`Backends::GL`](Backends::GL) in a native app on Windows, you must /// use [`ANGLE`](https://github.com/gfx-rs/wgpu#angle). This is because wgpu requires EGL to /// create a GL context without a window and only ANGLE supports that. -#[derive(Clone)] +#[derive(Resource, Clone)] pub struct WgpuSettings { pub device_label: Option>, pub backends: Option, diff --git a/crates/bevy_render/src/texture/fallback_image.rs b/crates/bevy_render/src/texture/fallback_image.rs index 97ede813ea27de..6f0a32e3604ac0 100644 --- a/crates/bevy_render/src/texture/fallback_image.rs +++ b/crates/bevy_render/src/texture/fallback_image.rs @@ -1,6 +1,6 @@ use crate::{render_resource::*, texture::DefaultImageSampler}; use bevy_derive::Deref; -use bevy_ecs::prelude::FromWorld; +use bevy_ecs::{prelude::FromWorld, system::Resource}; use bevy_math::Vec2; use wgpu::{Extent3d, TextureDimension, TextureFormat}; @@ -14,7 +14,7 @@ use crate::{ /// which can be used in situations where an image was not explicitly defined. The most common /// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures. /// [`FallbackImage`] defaults to a 1x1 fully white texture, making blending colors with it a no-op. -#[derive(Deref)] +#[derive(Resource, Deref)] pub struct FallbackImage(GpuImage); impl FromWorld for FallbackImage { diff --git a/crates/bevy_render/src/texture/image.rs b/crates/bevy_render/src/texture/image.rs index b4898e3ed65063..bb7138bddf022e 100644 --- a/crates/bevy_render/src/texture/image.rs +++ b/crates/bevy_render/src/texture/image.rs @@ -14,7 +14,7 @@ use crate::{ }; use bevy_asset::HandleUntyped; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem}; +use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem}; use bevy_math::Vec2; use bevy_reflect::TypeUuid; use std::hash::Hash; @@ -162,6 +162,7 @@ impl ImageSampler { /// Global resource for [`Image`] settings. /// /// Can be set via `insert_resource` during app initialization to change the default settings. +#[derive(Resource)] pub struct ImageSettings { /// The default image sampler to use when [`ImageSampler`] is set to `Default`. pub default_sampler: wgpu::SamplerDescriptor<'static>, @@ -194,7 +195,7 @@ impl ImageSettings { /// /// The [`ImageSettings`] resource can be set during app initialization to change the default /// image sampler. -#[derive(Debug, Clone, Deref, DerefMut)] +#[derive(Resource, Debug, Clone, Deref, DerefMut)] pub struct DefaultImageSampler(pub(crate) Sampler); impl Default for Image { diff --git a/crates/bevy_render/src/texture/texture_cache.rs b/crates/bevy_render/src/texture/texture_cache.rs index d92878774aee7f..30b031feede3ef 100644 --- a/crates/bevy_render/src/texture/texture_cache.rs +++ b/crates/bevy_render/src/texture/texture_cache.rs @@ -2,7 +2,7 @@ use crate::{ render_resource::{Texture, TextureView}, renderer::RenderDevice, }; -use bevy_ecs::prelude::ResMut; +use bevy_ecs::{prelude::ResMut, system::Resource}; use bevy_utils::{Entry, HashMap}; use wgpu::{TextureDescriptor, TextureViewDescriptor}; @@ -26,7 +26,7 @@ pub struct CachedTexture { /// This resource caches textures that are created repeatedly in the rendering process and /// are only required for one frame. -#[derive(Default)] +#[derive(Resource, Default)] pub struct TextureCache { textures: HashMap, Vec>, } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 71e3597e3127be..55ca6a31b6c235 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -58,7 +58,7 @@ impl Plugin for ViewPlugin { /// .insert_resource(Msaa { samples: 4 }) /// .run(); /// ``` -#[derive(Clone, ExtractResource, Reflect)] +#[derive(Resource, Clone, ExtractResource, Reflect)] #[reflect(Resource)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in @@ -105,7 +105,7 @@ pub struct ViewUniform { height: f32, } -#[derive(Default)] +#[derive(Resource, Default)] pub struct ViewUniforms { pub uniforms: DynamicUniformBuffer, } diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 4699cef2020068..bba940680f6e39 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -12,7 +12,7 @@ use std::ops::{Deref, DerefMut}; use wgpu::TextureFormat; /// Token to ensure a system runs on the main thread. -#[derive(Default)] +#[derive(Resource, Default)] pub struct NonSendMarker; pub struct WindowRenderPlugin; @@ -48,7 +48,7 @@ pub struct ExtractedWindow { pub size_changed: bool, } -#[derive(Default)] +#[derive(Default, Resource)] pub struct ExtractedWindows { pub windows: HashMap, } @@ -113,7 +113,7 @@ fn extract_windows( } } -#[derive(Default)] +#[derive(Resource, Default)] pub struct WindowSurfaces { surfaces: HashMap, /// List of windows that we have already called the initial `configure_surface` for diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 7328f5f3662675..1de7202bfab4e6 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -1,5 +1,6 @@ use crate::{serde::SceneSerializer, Scene, SceneSpawnError}; use anyhow::Result; +use bevy_app::AppTypeRegistry; use bevy_ecs::{ entity::EntityMap, reflect::{ReflectComponent, ReflectMapEntities}, @@ -84,7 +85,7 @@ impl DynamicScene { world: &mut World, entity_map: &mut EntityMap, ) -> Result<(), SceneSpawnError> { - let registry = world.resource::().clone(); + let registry = world.resource::().clone(); let type_registry = registry.read(); for scene_entity in &self.entities { diff --git a/crates/bevy_scene/src/scene_loader.rs b/crates/bevy_scene/src/scene_loader.rs index 2a44af0cf51cad..e13b2541eeb13c 100644 --- a/crates/bevy_scene/src/scene_loader.rs +++ b/crates/bevy_scene/src/scene_loader.rs @@ -1,5 +1,6 @@ use crate::serde::SceneDeserializer; use anyhow::Result; +use bevy_app::AppTypeRegistry; use bevy_asset::{AssetLoader, LoadContext, LoadedAsset}; use bevy_ecs::world::{FromWorld, World}; use bevy_reflect::TypeRegistryArc; @@ -13,9 +14,9 @@ pub struct SceneLoader { impl FromWorld for SceneLoader { fn from_world(world: &mut World) -> Self { - let type_registry = world.resource::(); + let type_registry = world.resource::(); SceneLoader { - type_registry: (*type_registry).clone(), + type_registry: type_registry.0.clone(), } } } diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 60d145c1f52bc6..d9bfcb213687e5 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -1,14 +1,14 @@ use crate::{DynamicScene, Scene}; +use bevy_app::AppTypeRegistry; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::{ entity::{Entity, EntityMap}, event::{Events, ManualEventReader}, reflect::{ReflectComponent, ReflectMapEntities}, - system::Command, + system::{Command, Resource}, world::{Mut, World}, }; use bevy_hierarchy::{AddChild, Parent}; -use bevy_reflect::TypeRegistryArc; use bevy_utils::{tracing::error, HashMap}; use thiserror::Error; use uuid::Uuid; @@ -27,7 +27,7 @@ impl InstanceId { } } -#[derive(Default)] +#[derive(Default, Resource)] pub struct SceneSpawner { spawned_scenes: HashMap, Vec>, spawned_dynamic_scenes: HashMap, Vec>, @@ -164,7 +164,7 @@ impl SceneSpawner { let mut instance_info = InstanceInfo { entity_map: EntityMap::default(), }; - let type_registry = world.resource::().clone(); + let type_registry = world.resource::().clone(); let type_registry = type_registry.read(); world.resource_scope(|world, scenes: Mut>| { let scene = diff --git a/crates/bevy_sprite/Cargo.toml b/crates/bevy_sprite/Cargo.toml index 4a19637c684d33..a18bcd7d1e2917 100644 --- a/crates/bevy_sprite/Cargo.toml +++ b/crates/bevy_sprite/Cargo.toml @@ -22,6 +22,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = [ bevy_render = { path = "../bevy_render", version = "0.9.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } +bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" } # other bytemuck = { version = "1.5", features = ["derive"] } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index f1cd52329bb6fc..bef567f81c1558 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -1,6 +1,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle}; use bevy_core_pipeline::core_2d::Transparent2d; +use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ entity::Entity, event::EventReader, @@ -8,7 +9,7 @@ use bevy_ecs::{ schedule::ParallelSystemDescriptorCoercion, system::{ lifetimeless::{Read, SQuery, SRes}, - Commands, Local, Query, Res, ResMut, SystemParamItem, + Commands, Local, Query, Res, ResMut, Resource, SystemParamItem, }, world::FromWorld, }; @@ -170,6 +171,7 @@ where } /// Render pipeline data for a given [`Material2d`] +#[derive(Resource)] pub struct Material2dPipeline { pub mesh2d_pipeline: Mesh2dPipeline, pub material2d_layout: BindGroupLayout, @@ -373,6 +375,7 @@ pub struct PreparedMaterial2d { pub key: T::Data, } +#[derive(Resource)] struct ExtractedMaterials2d { extracted: Vec<(Handle, M)>, removed: Vec>, @@ -388,7 +391,14 @@ impl Default for ExtractedMaterials2d { } /// Stores all prepared representations of [`Material2d`] assets for as long as they exist. -pub type RenderMaterials2d = HashMap, PreparedMaterial2d>; +#[derive(Resource, Deref, DerefMut)] +pub struct RenderMaterials2d(HashMap, PreparedMaterial2d>); + +impl Default for RenderMaterials2d { + fn default() -> Self { + Self(Default::default()) + } +} /// This system extracts all created or modified assets of the corresponding [`Material2d`] type /// into the "render world". diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 41188cd48679fd..18513f0d5362d1 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -148,7 +148,7 @@ pub fn extract_mesh2d( commands.insert_or_spawn_batch(values); } -#[derive(Clone)] +#[derive(Resource, Clone)] pub struct Mesh2dPipeline { pub view_layout: BindGroupLayout, pub mesh_layout: BindGroupLayout, @@ -379,6 +379,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { } } +#[derive(Resource)] pub struct Mesh2dBindGroup { pub value: BindGroup, } diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index fb07f730694475..c2f98b2aab2868 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -34,6 +34,7 @@ use bytemuck::{Pod, Zeroable}; use copyless::VecHelper; use fixedbitset::FixedBitSet; +#[derive(Resource)] pub struct SpritePipeline { view_layout: BindGroupLayout, material_layout: BindGroupLayout, @@ -190,12 +191,12 @@ pub struct ExtractedSprite { pub anchor: Vec2, } -#[derive(Default)] +#[derive(Resource, Default)] pub struct ExtractedSprites { pub sprites: Vec, } -#[derive(Default)] +#[derive(Resource, Default)] pub struct SpriteAssetEvents { pub images: Vec>, } @@ -303,6 +304,7 @@ struct ColoredSpriteVertex { pub color: [f32; 4], } +#[derive(Resource)] pub struct SpriteMeta { vertices: BufferVec, colored_vertices: BufferVec, @@ -341,7 +343,7 @@ pub struct SpriteBatch { colored: bool, } -#[derive(Default)] +#[derive(Resource, Default)] pub struct ImageBindGroups { values: HashMap, BindGroup>, } diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index bd7b0007cb82e9..ea72b965e6bab1 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -2,6 +2,7 @@ use std::hash::Hash; use ab_glyph::{PxScale, ScaleFont}; use bevy_asset::{Assets, Handle, HandleId}; +use bevy_ecs::system::Resource; use bevy_math::Vec2; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; @@ -14,6 +15,7 @@ use crate::{ TextAlignment, TextSection, }; +#[derive(Resource)] pub struct TextPipeline { brush: GlyphBrush, glyph_map: HashMap, diff --git a/crates/bevy_time/src/fixed_timestep.rs b/crates/bevy_time/src/fixed_timestep.rs index 78fdfca797c50f..8f1c44065c0afc 100644 --- a/crates/bevy_time/src/fixed_timestep.rs +++ b/crates/bevy_time/src/fixed_timestep.rs @@ -4,7 +4,7 @@ use bevy_ecs::{ component::ComponentId, query::Access, schedule::ShouldRun, - system::{IntoSystem, Res, ResMut, System}, + system::{IntoSystem, Res, ResMut, Resource, System}, world::World, }; use bevy_utils::HashMap; @@ -41,7 +41,7 @@ impl FixedTimestepState { /// A global resource that tracks the individual [`FixedTimestepState`]s /// for every labeled [`FixedTimestep`]. -#[derive(Default)] +#[derive(Default, Resource)] pub struct FixedTimesteps { fixed_timesteps: HashMap, } @@ -234,7 +234,8 @@ mod test { use std::ops::{Add, Mul}; use std::time::Duration; - type Count = usize; + #[derive(Resource)] + struct Count(usize); const LABEL: &str = "test_step"; #[test] @@ -245,7 +246,7 @@ mod test { time.update_with_instant(instance); world.insert_resource(time); world.insert_resource(FixedTimesteps::default()); - world.insert_resource::(0); + world.insert_resource(Count(0)); let mut schedule = Schedule::default(); schedule.add_stage( @@ -258,30 +259,30 @@ mod test { // if time does not progress, the step does not run schedule.run(&mut world); schedule.run(&mut world); - assert_eq!(0, *world.resource::()); + assert_eq!(0, world.resource::().0); assert_eq!(0., get_accumulator_deciseconds(&world)); // let's progress less than one step advance_time(&mut world, instance, 0.4); schedule.run(&mut world); - assert_eq!(0, *world.resource::()); + assert_eq!(0, world.resource::().0); assert_eq!(4., get_accumulator_deciseconds(&world)); // finish the first step with 0.1s above the step length advance_time(&mut world, instance, 0.6); schedule.run(&mut world); - assert_eq!(1, *world.resource::()); + assert_eq!(1, world.resource::().0); assert_eq!(1., get_accumulator_deciseconds(&world)); // runs multiple times if the delta is multiple step lengths advance_time(&mut world, instance, 1.7); schedule.run(&mut world); - assert_eq!(3, *world.resource::()); + assert_eq!(3, world.resource::().0); assert_eq!(2., get_accumulator_deciseconds(&world)); } fn fixed_update(mut count: ResMut) { - *count += 1; + count.0 += 1; } fn advance_time(world: &mut World, instance: Instant, seconds: f32) { diff --git a/crates/bevy_time/src/lib.rs b/crates/bevy_time/src/lib.rs index 59823051597663..7da0e54a07f75e 100644 --- a/crates/bevy_time/src/lib.rs +++ b/crates/bevy_time/src/lib.rs @@ -46,8 +46,11 @@ impl Plugin for TimePlugin { } /// Channel resource used to receive time from render world +#[derive(Resource)] pub struct TimeReceiver(pub Receiver); + /// Channel resource used to send time from render world +#[derive(Resource)] pub struct TimeSender(pub Sender); /// Creates channels used for sending time between render world and app world diff --git a/crates/bevy_time/src/time.rs b/crates/bevy_time/src/time.rs index cfe34316a745ac..8cb864882f25e4 100644 --- a/crates/bevy_time/src/time.rs +++ b/crates/bevy_time/src/time.rs @@ -1,9 +1,9 @@ -use bevy_ecs::reflect::ReflectResource; +use bevy_ecs::{reflect::ReflectResource, system::Resource}; use bevy_reflect::Reflect; use bevy_utils::{Duration, Instant}; /// Tracks elapsed time since the last update and since the App has started -#[derive(Reflect, Debug, Clone)] +#[derive(Resource, Reflect, Debug, Clone)] #[reflect(Resource)] pub struct Time { delta: Duration, @@ -54,6 +54,7 @@ impl Time { /// # fn main () { /// # test_health_system(); /// # } + /// #[derive(Resource)] /// struct Health { /// // Health value between 0.0 and 1.0 /// health_value: f32, diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 85d8134c9d063a..e8ee3fc2db3f0b 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -5,7 +5,7 @@ use bevy_ecs::{ entity::Entity, event::EventReader, query::{Changed, With, Without, WorldQuery}, - system::{Query, Res, ResMut}, + system::{Query, Res, ResMut, Resource}, }; use bevy_hierarchy::{Children, Parent}; use bevy_log::warn; @@ -16,6 +16,7 @@ use bevy_window::{Window, WindowId, WindowScaleFactorChanged, Windows}; use std::fmt; use taffy::{number::Number, Taffy}; +#[derive(Resource)] pub struct FlexSurface { entity_to_taffy: HashMap, window_nodes: HashMap, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index e19d7b1731667f..036aa51997e17e 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -168,7 +168,7 @@ pub struct ExtractedUiNode { pub clip: Option, } -#[derive(Default)] +#[derive(Resource, Default)] pub struct ExtractedUiNodes { pub uinodes: Vec, } @@ -341,6 +341,7 @@ struct UiVertex { pub color: [f32; 4], } +#[derive(Resource)] pub struct UiMeta { vertices: BufferVec, view_bind_group: Option, @@ -504,7 +505,7 @@ pub fn prepare_uinodes( ui_meta.vertices.write_buffer(&render_device, &render_queue); } -#[derive(Default)] +#[derive(Resource, Default)] pub struct UiImageBindGroups { pub values: HashMap, BindGroup>, } diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index 4404429afffe78..e199cef521e0bd 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -3,6 +3,7 @@ use bevy_render::{ render_resource::*, renderer::RenderDevice, texture::BevyDefault, view::ViewUniform, }; +#[derive(Resource)] pub struct UiPipeline { pub view_layout: BindGroupLayout, pub image_layout: BindGroupLayout, diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 02db232f9a6b86..005675f2ae26f3 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -25,13 +25,14 @@ use bevy_app::prelude::*; use bevy_ecs::{ event::Events, schedule::{ParallelSystemDescriptorCoercion, SystemLabel}, + system::Resource, }; /// The configuration information for the [`WindowPlugin`]. /// /// It can be added as a [`Resource`](bevy_ecs::system::Resource) before the [`WindowPlugin`] /// runs, to configure how it behaves. -#[derive(Clone)] +#[derive(Resource, Clone)] pub struct WindowSettings { /// Whether to create a window when added. /// diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index a12ca415de64c7..2ab7761b92be93 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -1,3 +1,4 @@ +use bevy_ecs::system::Resource; use bevy_math::{DVec2, IVec2, UVec2, Vec2}; use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{tracing::warn, Uuid}; @@ -777,7 +778,7 @@ pub enum MonitorSelection { /// See [`examples/window/window_settings.rs`] for usage. /// /// [`examples/window/window_settings.rs`]: https://github.com/bevyengine/bevy/blob/latest/examples/window/window_settings.rs -#[derive(Debug, Clone)] +#[derive(Resource, Debug, Clone)] pub struct WindowDescriptor { /// The requested logical width of the window's client area. /// diff --git a/crates/bevy_window/src/windows.rs b/crates/bevy_window/src/windows.rs index 608009b7b2c52a..29efe73876d83f 100644 --- a/crates/bevy_window/src/windows.rs +++ b/crates/bevy_window/src/windows.rs @@ -1,8 +1,9 @@ use super::{Window, WindowId}; +use bevy_ecs::prelude::Resource; use bevy_utils::HashMap; /// A collection of [`Window`]s with unique [`WindowId`]s. -#[derive(Debug, Default)] +#[derive(Debug, Default, Resource)] pub struct Windows { windows: HashMap, } diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 135ad3326d86a7..b48dd441146d7b 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -316,7 +316,7 @@ impl Default for WinitPersistentState { } } -#[derive(Default)] +#[derive(Default, Resource)] struct WinitCreateWindowReader(ManualEventReader); pub fn winit_runner_with(mut app: App) { diff --git a/crates/bevy_winit/src/web_resize.rs b/crates/bevy_winit/src/web_resize.rs index 637dc06e0eaed4..7e289afdfc6752 100644 --- a/crates/bevy_winit/src/web_resize.rs +++ b/crates/bevy_winit/src/web_resize.rs @@ -20,13 +20,14 @@ struct ResizeEvent { window_id: WindowId, } +#[derive(Resource)] pub(crate) struct CanvasParentResizeEventChannel { sender: Sender, receiver: Receiver, } fn canvas_parent_resize_event_handler( - winit_windows: Res, + winit_windows: NonSend, resize_events: Res, ) { for event in resize_events.receiver.try_iter() { diff --git a/crates/bevy_winit/src/winit_config.rs b/crates/bevy_winit/src/winit_config.rs index 9f40db04f2fc40..c1db2b2fd27e68 100644 --- a/crates/bevy_winit/src/winit_config.rs +++ b/crates/bevy_winit/src/winit_config.rs @@ -1,7 +1,8 @@ +use bevy_ecs::system::Resource; use bevy_utils::Duration; /// A resource for configuring usage of the `rust_winit` library. -#[derive(Debug)] +#[derive(Debug, Resource)] pub struct WinitSettings { /// Configures the winit library to return control to the main thread after the /// [run](bevy_app::App::run) loop is exited. Winit strongly recommends avoiding this when diff --git a/errors/B0003.md b/errors/B0003.md index 829c4b76f7f8e0..1d19fc1bc13594 100644 --- a/errors/B0003.md +++ b/errors/B0003.md @@ -16,6 +16,7 @@ fn main() { .run(); } +#[derive(Resource)] struct MyEntity(Entity); #[derive(Component)] diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index d5c245f7bd7280..139b88ddea46ec 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -116,6 +116,7 @@ fn star( pub struct ColoredMesh2d; /// Custom pipeline for 2d meshes with vertex colors +#[derive(Resource)] pub struct ColoredMesh2dPipeline { /// this pipeline wraps the standard [`Mesh2dPipeline`] mesh2d_pipeline: Mesh2dPipeline, diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index 4889354da7c618..9f5c77a7bf2f00 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -21,7 +21,7 @@ enum AppState { Finished, } -#[derive(Default)] +#[derive(Resource, Default)] struct RpgSpriteHandles { handles: Vec, } diff --git a/examples/3d/skybox.rs b/examples/3d/skybox.rs index 13ccfa3d1f3c4c..ae6aa9a0f86c66 100644 --- a/examples/3d/skybox.rs +++ b/examples/3d/skybox.rs @@ -52,6 +52,7 @@ fn main() { .run(); } +#[derive(Resource)] struct Cubemap { is_loaded: bool, index: usize, diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index 533fd1677c562b..0e65da39a0dd3d 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -15,6 +15,7 @@ fn main() { .run(); } +#[derive(Resource)] struct Animations(Vec>); fn setup( diff --git a/examples/app/custom_loop.rs b/examples/app/custom_loop.rs index 8e6d655cd891f9..17d7eca4c2d15c 100644 --- a/examples/app/custom_loop.rs +++ b/examples/app/custom_loop.rs @@ -4,6 +4,7 @@ use bevy::prelude::*; use std::io; +#[derive(Resource)] struct Input(String); fn my_runner(mut app: App) { diff --git a/examples/app/plugin.rs b/examples/app/plugin.rs index e154ecd236a339..eeff7c72d998fc 100644 --- a/examples/app/plugin.rs +++ b/examples/app/plugin.rs @@ -35,6 +35,7 @@ impl Plugin for PrintMessagePlugin { } } +#[derive(Resource)] struct PrintMessageState { message: String, timer: Timer, diff --git a/examples/asset/custom_asset.rs b/examples/asset/custom_asset.rs index a8368c07a4bf29..50089d5dfb5fb9 100644 --- a/examples/asset/custom_asset.rs +++ b/examples/asset/custom_asset.rs @@ -46,7 +46,7 @@ fn main() { .run(); } -#[derive(Default)] +#[derive(Resource, Default)] struct State { handle: Handle, printed: bool, diff --git a/examples/async_tasks/async_compute.rs b/examples/async_tasks/async_compute.rs index aee0d356acbf5f..e96b46030f8cc4 100644 --- a/examples/async_tasks/async_compute.rs +++ b/examples/async_tasks/async_compute.rs @@ -22,9 +22,10 @@ fn main() { // Number of cubes to spawn across the x, y, and z axis const NUM_CUBES: u32 = 6; -#[derive(Deref)] +#[derive(Resource, Deref)] struct BoxMeshHandle(Handle); -#[derive(Deref)] + +#[derive(Resource, Deref)] struct BoxMaterialHandle(Handle); /// Startup system which runs only once and generates our Box Mesh diff --git a/examples/async_tasks/external_source_external_thread.rs b/examples/async_tasks/external_source_external_thread.rs index 679d67d6a626b5..91e6ff92cc9c38 100644 --- a/examples/async_tasks/external_source_external_thread.rs +++ b/examples/async_tasks/external_source_external_thread.rs @@ -17,11 +17,11 @@ fn main() { .run(); } -#[derive(Deref)] +#[derive(Resource, Deref)] struct StreamReceiver(Receiver); struct StreamEvent(u32); -#[derive(Deref)] +#[derive(Resource, Deref)] struct LoadedFont(Handle); fn setup(mut commands: Commands, asset_server: Res) { diff --git a/examples/audio/audio_control.rs b/examples/audio/audio_control.rs index e5f0728516af86..2c5da2c919936c 100644 --- a/examples/audio/audio_control.rs +++ b/examples/audio/audio_control.rs @@ -23,6 +23,7 @@ fn setup( commands.insert_resource(MusicController(handle)); } +#[derive(Resource)] struct MusicController(Handle); fn update_speed( diff --git a/examples/ecs/ecs_guide.rs b/examples/ecs/ecs_guide.rs index d2772a178d864c..e8c9e853a4a1f4 100644 --- a/examples/ecs/ecs_guide.rs +++ b/examples/ecs/ecs_guide.rs @@ -52,7 +52,7 @@ struct Score { // // This resource holds information about the game: -#[derive(Default)] +#[derive(Resource, Default)] struct GameState { current_round: usize, total_players: usize, @@ -60,6 +60,7 @@ struct GameState { } // This resource provides rules for our "game". +#[derive(Resource)] struct GameRules { winning_score: usize, max_rounds: usize, diff --git a/examples/ecs/event.rs b/examples/ecs/event.rs index f4c93c44d47a07..cc9fa8376d0ab1 100644 --- a/examples/ecs/event.rs +++ b/examples/ecs/event.rs @@ -22,6 +22,7 @@ struct MyEvent { #[derive(Default)] struct PlaySound; +#[derive(Resource)] struct EventTriggerState { event_timer: Timer, } diff --git a/examples/ecs/state.rs b/examples/ecs/state.rs index f5a1bd161d1ab1..db34e5358d2499 100644 --- a/examples/ecs/state.rs +++ b/examples/ecs/state.rs @@ -26,6 +26,7 @@ enum AppState { InGame, } +#[derive(Resource)] struct MenuData { button_entity: Entity, } diff --git a/examples/ecs/system_chaining.rs b/examples/ecs/system_chaining.rs index 65b0401aa24e01..8046dcd7ec3d17 100644 --- a/examples/ecs/system_chaining.rs +++ b/examples/ecs/system_chaining.rs @@ -11,7 +11,7 @@ fn main() { .run(); } -#[derive(Deref)] +#[derive(Resource, Deref)] struct Message(String); // this system produces a Result output by trying to parse the Message resource diff --git a/examples/ecs/system_param.rs b/examples/ecs/system_param.rs index 176caa716d629f..dace30d69ce2f5 100644 --- a/examples/ecs/system_param.rs +++ b/examples/ecs/system_param.rs @@ -12,7 +12,8 @@ fn main() { #[derive(Component)] pub struct Player; -#[derive(Component)] + +#[derive(Resource)] pub struct PlayerCount(usize); /// The [`SystemParam`] struct can contain any types that can also be included in a diff --git a/examples/ecs/system_sets.rs b/examples/ecs/system_sets.rs index 38395ddf10d239..035103ca2789e5 100644 --- a/examples/ecs/system_sets.rs +++ b/examples/ecs/system_sets.rs @@ -36,7 +36,7 @@ struct Physics; struct PostPhysics; /// Resource used to stop our example. -#[derive(Default)] +#[derive(Resource, Default)] struct Done(bool); fn main() { diff --git a/examples/ecs/timers.rs b/examples/ecs/timers.rs index 6adeb76040e4f7..dd75acc6831271 100644 --- a/examples/ecs/timers.rs +++ b/examples/ecs/timers.rs @@ -15,6 +15,7 @@ fn main() { #[derive(Component, Deref, DerefMut)] pub struct PrintOnCompletionTimer(Timer); +#[derive(Resource)] pub struct Countdown { pub percent_trigger: Timer, pub main_timer: Timer, diff --git a/examples/games/alien_cake_addict.rs b/examples/games/alien_cake_addict.rs index f0a6b4e0c05cfb..b634b5786dd656 100644 --- a/examples/games/alien_cake_addict.rs +++ b/examples/games/alien_cake_addict.rs @@ -56,7 +56,7 @@ struct Bonus { handle: Handle, } -#[derive(Default)] +#[derive(Resource, Default)] struct Game { board: Vec>, player: Player, diff --git a/examples/games/breakout.rs b/examples/games/breakout.rs index ebc69952d41ce7..cbabf086bb13e7 100644 --- a/examples/games/breakout.rs +++ b/examples/games/breakout.rs @@ -88,6 +88,7 @@ struct CollisionEvent; #[derive(Component)] struct Brick; +#[derive(Resource)] struct CollisionSound(Handle); // This bundle is a collection of the components that define a "wall" in our game @@ -164,6 +165,7 @@ impl WallBundle { } // This resource tracks the game's score +#[derive(Resource)] struct Scoreboard { score: usize, } diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index ff9ff1b32a2d18..f53670336022a8 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -24,11 +24,13 @@ fn main() { // Store contributors in a collection that preserves the uniqueness type Contributors = HashSet; +#[derive(Resource)] struct ContributorSelection { order: Vec, idx: usize, } +#[derive(Resource)] struct SelectionState { timer: Timer, has_triggered: bool, diff --git a/examples/games/game_menu.rs b/examples/games/game_menu.rs index 7868929d434586..325c8ea4c3f040 100644 --- a/examples/games/game_menu.rs +++ b/examples/games/game_menu.rs @@ -15,7 +15,7 @@ enum GameState { } // One of the two settings that can be set through the menu. It will be a resource in the app -#[derive(Debug, Component, PartialEq, Eq, Clone, Copy)] +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] enum DisplayQuality { Low, Medium, @@ -23,7 +23,7 @@ enum DisplayQuality { } // One of the two settings that can be set through the menu. It will be a resource in the app -#[derive(Debug, Component, PartialEq, Eq, Clone, Copy)] +#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] struct Volume(u32); fn main() { @@ -75,7 +75,7 @@ mod splash { struct OnSplashScreen; // Newtype to use a `Timer` for this screen as a resource - #[derive(Deref, DerefMut)] + #[derive(Resource, Deref, DerefMut)] struct SplashTimer(Timer); fn splash_setup(mut commands: Commands, asset_server: Res) { @@ -133,7 +133,7 @@ mod game { #[derive(Component)] struct OnGameScreen; - #[derive(Deref, DerefMut)] + #[derive(Resource, Deref, DerefMut)] struct GameTimer(Timer); fn game_setup( @@ -362,7 +362,7 @@ mod menu { // This system updates the settings when a new value for a setting is selected, and marks // the button as the one currently selected - fn setting_button( + fn setting_button( interaction_query: Query<(&Interaction, &T, Entity), (Changed, With