Skip to content

Commit 9ea6b13

Browse files
Always enable state scoped entities (#20883)
# Objective - Reverts #16180 - Fixes #20866 ## Solution - Just enable state scoped entities all the time. No need to keep API surface for disabling that tiiiiiny bit of overhead. If someone needs this, we can still trivially add it back. ## Testing - `bevy run --example state_scope` --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent d6421f8 commit 9ea6b13

File tree

6 files changed

+44
-107
lines changed

6 files changed

+44
-107
lines changed

crates/bevy_state/macros/src/states.rs

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,12 @@
11
use proc_macro::TokenStream;
22
use quote::{format_ident, quote};
3-
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, LitBool, Pat, Path, Result};
3+
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Pat, Path, Result};
44

55
use crate::bevy_state_path;
66

7-
pub const STATES: &str = "states";
8-
pub const SCOPED_ENTITIES: &str = "scoped_entities";
9-
10-
struct StatesAttrs {
11-
scoped_entities_enabled: bool,
12-
}
13-
14-
fn parse_states_attr(ast: &DeriveInput) -> Result<StatesAttrs> {
15-
let mut attrs = StatesAttrs {
16-
scoped_entities_enabled: true,
17-
};
18-
19-
for attr in ast.attrs.iter() {
20-
if attr.path().is_ident(STATES) {
21-
attr.parse_nested_meta(|nested| {
22-
if nested.path.is_ident(SCOPED_ENTITIES) {
23-
if let Ok(value) = nested.value() {
24-
attrs.scoped_entities_enabled = value.parse::<LitBool>()?.value();
25-
}
26-
Ok(())
27-
} else {
28-
Err(nested.error("Unsupported attribute"))
29-
}
30-
})?;
31-
}
32-
}
33-
34-
Ok(attrs)
35-
}
36-
377
pub fn derive_states(input: TokenStream) -> TokenStream {
388
let ast = parse_macro_input!(input as DeriveInput);
399

40-
let attrs = match parse_states_attr(&ast) {
41-
Ok(attrs) => attrs,
42-
Err(e) => return e.into_compile_error().into(),
43-
};
44-
4510
let generics = ast.generics;
4611
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
4712

@@ -58,11 +23,8 @@ pub fn derive_states(input: TokenStream) -> TokenStream {
5823

5924
let struct_name = &ast.ident;
6025

61-
let scoped_entities_enabled = attrs.scoped_entities_enabled;
62-
6326
quote! {
6427
impl #impl_generics #trait_path for #struct_name #ty_generics #where_clause {
65-
const SCOPED_ENTITIES_ENABLED: bool = #scoped_entities_enabled;
6628
}
6729

6830
impl #impl_generics #state_mutation_trait_path for #struct_name #ty_generics #where_clause {
@@ -76,7 +38,7 @@ struct Source {
7638
source_value: Pat,
7739
}
7840

79-
fn parse_sources_attr(ast: &DeriveInput) -> Result<(StatesAttrs, Source)> {
41+
fn parse_sources_attr(ast: &DeriveInput) -> Result<Source> {
8042
let mut result = ast
8143
.attrs
8244
.iter()
@@ -112,19 +74,16 @@ fn parse_sources_attr(ast: &DeriveInput) -> Result<(StatesAttrs, Source)> {
11274
));
11375
}
11476

115-
let states_attrs = parse_states_attr(ast)?;
116-
11777
let Some(result) = result.pop() else {
11878
return Err(syn::Error::new(ast.span(), "SubStates require a source"));
11979
};
12080

121-
Ok((states_attrs, result))
81+
Ok(result)
12282
}
12383

12484
pub fn derive_substates(input: TokenStream) -> TokenStream {
12585
let ast = parse_macro_input!(input as DeriveInput);
126-
let (states_attrs, sources) =
127-
parse_sources_attr(&ast).expect("Failed to parse substate sources");
86+
let sources = parse_sources_attr(&ast).expect("Failed to parse substate sources");
12887

12988
let generics = ast.generics;
13089
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@@ -155,8 +114,6 @@ pub fn derive_substates(input: TokenStream) -> TokenStream {
155114
let source_state_type = sources.source_type;
156115
let source_state_value = sources.source_value;
157116

158-
let scoped_entities_enabled = states_attrs.scoped_entities_enabled;
159-
160117
let result = quote! {
161118
impl #impl_generics #trait_path for #struct_name #ty_generics #where_clause {
162119
type SourceStates = #source_state_type;
@@ -168,8 +125,6 @@ pub fn derive_substates(input: TokenStream) -> TokenStream {
168125

169126
impl #impl_generics #state_trait_path for #struct_name #ty_generics #where_clause {
170127
const DEPENDENCY_DEPTH : usize = <Self as #trait_path>::SourceStates::SET_DEPENDENCY_DEPTH + 1;
171-
172-
const SCOPED_ENTITIES_ENABLED: bool = #scoped_entities_enabled;
173128
}
174129

175130
impl #impl_generics #state_mutation_trait_path for #struct_name #ty_generics #where_clause {

crates/bevy_state/src/app.rs

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ pub trait AppExtStates {
5959

6060
/// Enable state-scoped entity clearing for state `S`.
6161
///
62-
/// This is enabled by default. If you don't want this behavior, add the `#[states(scoped_entities = false)]`
63-
/// attribute when deriving the [`States`] trait.
64-
///
65-
/// For more information refer to [`crate::state_scoped`].
62+
/// Since state scoped entities are enabled by default, this method does nothing anymore.
6663
#[doc(hidden)]
64+
#[deprecated(
65+
since = "0.17.0",
66+
note = "State scoped entities are enabled by default. This method does nothing anymore, you can safely remove it."
67+
)]
6768
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self;
6869

6970
#[cfg(feature = "bevy_reflect")]
@@ -111,9 +112,7 @@ impl AppExtStates for SubApp {
111112
exited: None,
112113
entered: Some(state),
113114
});
114-
if S::SCOPED_ENTITIES_ENABLED {
115-
self.enable_state_scoped_entities::<S>();
116-
}
115+
enable_state_scoped_entities::<S>(self);
117116
} else {
118117
let name = core::any::type_name::<S>();
119118
warn!("State {name} is already initialized.");
@@ -136,9 +135,7 @@ impl AppExtStates for SubApp {
136135
exited: None,
137136
entered: Some(state),
138137
});
139-
if S::SCOPED_ENTITIES_ENABLED {
140-
self.enable_state_scoped_entities::<S>();
141-
}
138+
enable_state_scoped_entities::<S>(self);
142139
} else {
143140
// Overwrite previous state and initial event
144141
self.insert_resource::<State<S>>(State::new(state.clone()));
@@ -173,9 +170,7 @@ impl AppExtStates for SubApp {
173170
exited: None,
174171
entered: state,
175172
});
176-
if S::SCOPED_ENTITIES_ENABLED {
177-
self.enable_state_scoped_entities::<S>();
178-
}
173+
enable_state_scoped_entities::<S>(self);
179174
} else {
180175
let name = core::any::type_name::<S>();
181176
warn!("Computed state {name} is already initialized.");
@@ -204,9 +199,7 @@ impl AppExtStates for SubApp {
204199
exited: None,
205200
entered: state,
206201
});
207-
if S::SCOPED_ENTITIES_ENABLED {
208-
self.enable_state_scoped_entities::<S>();
209-
}
202+
enable_state_scoped_entities::<S>(self);
210203
} else {
211204
let name = core::any::type_name::<S>();
212205
warn!("Sub state {name} is already initialized.");
@@ -217,28 +210,7 @@ impl AppExtStates for SubApp {
217210

218211
#[doc(hidden)]
219212
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
220-
if !self
221-
.world()
222-
.contains_resource::<Events<StateTransitionEvent<S>>>()
223-
{
224-
let name = core::any::type_name::<S>();
225-
warn!("State scoped entities are enabled for state `{name}`, but the state isn't installed in the app!");
226-
}
227-
228-
// Note: We work with `StateTransition` in set
229-
// `StateTransitionSystems::ExitSchedules` rather than `OnExit`, because
230-
// `OnExit` only runs for one specific variant of the state.
231-
self.add_systems(
232-
StateTransition,
233-
despawn_entities_on_exit_state::<S>.in_set(StateTransitionSystems::ExitSchedules),
234-
)
235-
// Note: We work with `StateTransition` in set
236-
// `StateTransitionSystems::EnterSchedules` rather than `OnEnter`, because
237-
// `OnEnter` only runs for one specific variant of the state.
238-
.add_systems(
239-
StateTransition,
240-
despawn_entities_on_enter_state::<S>.in_set(StateTransitionSystems::EnterSchedules),
241-
)
213+
self
242214
}
243215

244216
#[cfg(feature = "bevy_reflect")]
@@ -266,6 +238,31 @@ impl AppExtStates for SubApp {
266238
}
267239
}
268240

241+
fn enable_state_scoped_entities<S: States>(app: &mut SubApp) {
242+
if !app
243+
.world()
244+
.contains_resource::<Events<StateTransitionEvent<S>>>()
245+
{
246+
let name = core::any::type_name::<S>();
247+
warn!("State scoped entities are enabled for state `{name}`, but the state wasn't initialized in the app!");
248+
}
249+
250+
// Note: We work with `StateTransition` in set
251+
// `StateTransitionSystems::ExitSchedules` rather than `OnExit`, because
252+
// `OnExit` only runs for one specific variant of the state.
253+
app.add_systems(
254+
StateTransition,
255+
despawn_entities_on_exit_state::<S>.in_set(StateTransitionSystems::ExitSchedules),
256+
)
257+
// Note: We work with `StateTransition` in set
258+
// `StateTransitionSystems::EnterSchedules` rather than `OnEnter`, because
259+
// `OnEnter` only runs for one specific variant of the state.
260+
.add_systems(
261+
StateTransition,
262+
despawn_entities_on_enter_state::<S>.in_set(StateTransitionSystems::EnterSchedules),
263+
);
264+
}
265+
269266
impl AppExtStates for App {
270267
fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self {
271268
self.main_mut().init_state::<S>();
@@ -289,7 +286,6 @@ impl AppExtStates for App {
289286

290287
#[doc(hidden)]
291288
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
292-
self.main_mut().enable_state_scoped_entities::<S>();
293289
self
294290
}
295291

crates/bevy_state/src/state/computed_states.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ pub trait ComputedStates: 'static + Send + Sync + Clone + PartialEq + Eq + Hash
9191

9292
impl<S: ComputedStates> States for S {
9393
const DEPENDENCY_DEPTH: usize = S::SourceStates::SET_DEPENDENCY_DEPTH + 1;
94-
95-
const SCOPED_ENTITIES_ENABLED: bool = true;
9694
}
9795

9896
#[cfg(test)]

crates/bevy_state/src/state/states.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,4 @@ pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug
6666
/// Used to help order transitions and de-duplicate [`ComputedStates`](crate::state::ComputedStates), as well as prevent cyclical
6767
/// `ComputedState` dependencies.
6868
const DEPENDENCY_DEPTH: usize = 1;
69-
70-
/// Should [state scoping](crate::state_scoped) be enabled for this state?
71-
/// If set to `true`, the
72-
/// [`DespawnOnEnter`](crate::state_scoped::DespawnOnEnter) and
73-
/// [`DespawnOnExit`](crate::state_scoped::DespawnOnExit)
74-
/// components are used to remove entities when entering or exiting the
75-
/// state.
76-
const SCOPED_ENTITIES_ENABLED: bool = false;
7769
}

crates/bevy_state/src/state_scoped.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,6 @@ pub fn despawn_entities_on_exit_state<S: States>(
9292
/// Entities marked with this component will be despawned
9393
/// upon entering the given state.
9494
///
95-
/// To enable this feature remember to configure your application
96-
/// with [`enable_state_scoped_entities`](crate::app::AppExtStates::enable_state_scoped_entities) on your state(s) of choice.
97-
///
9895
/// ```
9996
/// use bevy_state::prelude::*;
10097
/// use bevy_ecs::{prelude::*, system::ScheduleSystem};
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
---
2-
title: Entities are now state scoped by default
3-
pull_requests: [19354]
2+
title: State-scoped entities are now always enabled implicitly
3+
pull_requests: [19354, 20883]
44
---
55

6-
State scoped entities is now enabled by default, and you don't need to call `app.enable_state_scoped_entities::<State>()` anymore.
6+
State scoped entities is now always enabled, and as a consequence, `app.enable_state_scoped_entities::<State>()` is no longer needed.
7+
It has been marked as deprecated and does nothing when called.
78

8-
If you were previously adding the `#[states(scoped_entities)]` attribute when deriving the `States` trait, you can remove it.
9-
10-
If you want to keep the previous behavior, you must add the attribute `#[states(scoped_entities = false)]`.
9+
The attribute `#[states(scoped_entities)]` has been removed. You can safely remove it from your code without replacement.

0 commit comments

Comments
 (0)