bevy/crates/bevy_state/src/state_scoped_events.rs
Zachary Harrold 9bc0ae33c3
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective

- Contributes to #16877

## Solution

- Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils`
and into `bevy_platform_support`
- Refactored the above to match the layout of these types in `std`.
- Updated crates as required.

## Testing

- CI

---

## Migration Guide

- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::hash`:
  - `FixedState`
  - `DefaultHasher`
  - `RandomState`
  - `FixedHasher`
  - `Hashed`
  - `PassHash`
  - `PassHasher`
  - `NoOpHash`
- The following items were moved out of `bevy_utils` and into
`bevy_platform_support::collections`:
  - `HashMap`
  - `HashSet`
- `bevy_utils::hashbrown` has been removed. Instead, import from
`bevy_platform_support::collections` _or_ take a dependency on
`hashbrown` directly.
- `bevy_utils::Entry` has been removed. Instead, import from
`bevy_platform_support::collections::hash_map` or
`bevy_platform_support::collections::hash_set` as appropriate.
- All of the above equally apply to `bevy::utils` and
`bevy::platform_support`.

## Notes

- I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils`
as they might be candidates for micro-crating. They can always be moved
into `bevy_platform_support` at a later date if desired.
2025-01-23 16:46:08 +00:00

112 lines
3.3 KiB
Rust

use alloc::vec::Vec;
use core::marker::PhantomData;
use bevy_app::{App, SubApp};
use bevy_ecs::{
event::{Event, EventReader, Events},
resource::Resource,
system::Commands,
world::World,
};
use bevy_platform_support::collections::HashMap;
use crate::state::{FreelyMutableState, OnExit, StateTransitionEvent};
fn clear_event_queue<E: Event>(w: &mut World) {
if let Some(mut queue) = w.get_resource_mut::<Events<E>>() {
queue.clear();
}
}
#[derive(Resource)]
struct StateScopedEvents<S: FreelyMutableState> {
cleanup_fns: HashMap<S, Vec<fn(&mut World)>>,
}
impl<S: FreelyMutableState> StateScopedEvents<S> {
fn add_event<E: Event>(&mut self, state: S) {
self.cleanup_fns
.entry(state)
.or_default()
.push(clear_event_queue::<E>);
}
fn cleanup(&self, w: &mut World, state: S) {
let Some(fns) = self.cleanup_fns.get(&state) else {
return;
};
for callback in fns {
(*callback)(w);
}
}
}
impl<S: FreelyMutableState> Default for StateScopedEvents<S> {
fn default() -> Self {
Self {
cleanup_fns: HashMap::default(),
}
}
}
fn cleanup_state_scoped_event<S: FreelyMutableState>(
mut c: Commands,
mut transitions: EventReader<StateTransitionEvent<S>>,
) {
let Some(transition) = transitions.read().last() else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(exited) = transition.exited.clone() else {
return;
};
c.queue(move |w: &mut World| {
w.resource_scope::<StateScopedEvents<S>, ()>(|w, events| {
events.cleanup(w, exited);
});
});
}
fn add_state_scoped_event_impl<E: Event, S: FreelyMutableState>(
app: &mut SubApp,
_p: PhantomData<E>,
state: S,
) {
if !app.world().contains_resource::<StateScopedEvents<S>>() {
app.init_resource::<StateScopedEvents<S>>();
}
app.add_event::<E>();
app.world_mut()
.resource_mut::<StateScopedEvents<S>>()
.add_event::<E>(state.clone());
app.add_systems(OnExit(state), cleanup_state_scoped_event::<S>);
}
/// Extension trait for [`App`] adding methods for registering state scoped events.
pub trait StateScopedEventsAppExt {
/// Adds an [`Event`] that is automatically cleaned up when leaving the specified `state`.
///
/// Note that event cleanup is ordered ambiguously relative to [`StateScoped`](crate::prelude::StateScoped) entity
/// cleanup and the [`OnExit`] schedule for the target state. All of these (state scoped
/// entities and events cleanup, and `OnExit`) occur within schedule [`StateTransition`](crate::prelude::StateTransition)
/// and system set `StateTransitionSteps::ExitSchedules`.
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self;
}
impl StateScopedEventsAppExt for App {
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self {
add_state_scoped_event_impl(self.main_mut(), PhantomData::<E>, state);
self
}
}
impl StateScopedEventsAppExt for SubApp {
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self {
add_state_scoped_event_impl(self, PhantomData::<E>, state);
self
}
}