This pr uses the `extern crate self as` trick to make proc macros behave the same way inside and outside bevy. # Objective - Removes noise introduced by `crate as` in the whole bevy repo. - Fixes #17004. - Hardens proc macro path resolution. ## TODO - [x] `BevyManifest` needs cleanup. - [x] Cleanup remaining `crate as`. - [x] Add proper integration tests to the ci. ## Notes - `cargo-manifest-proc-macros` is written by me and based/inspired by the old `BevyManifest` implementation and [`bkchr/proc-macro-crate`](https://github.com/bkchr/proc-macro-crate). - What do you think about the new integration test machinery I added to the `ci`? More and better integration tests can be added at a later stage. The goal of these integration tests is to simulate an actual separate crate that uses bevy. Ideally they would lightly touch all bevy crates. ## Testing - Needs RA test - Needs testing from other users - Others need to run at least `cargo run -p ci integration-test` and verify that they work. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
94 lines
4.0 KiB
Rust
94 lines
4.0 KiB
Rust
use alloc::vec::Vec;
|
|
use bevy_ecs::{
|
|
change_detection::{DetectChangesMut, MutUntyped},
|
|
component::{ComponentId, Tick},
|
|
event::{Event, Events},
|
|
resource::Resource,
|
|
world::World,
|
|
};
|
|
|
|
#[doc(hidden)]
|
|
struct RegisteredEvent {
|
|
component_id: ComponentId,
|
|
// Required to flush the secondary buffer and drop events even if left unchanged.
|
|
previously_updated: bool,
|
|
// SAFETY: The component ID and the function must be used to fetch the Events<T> resource
|
|
// of the same type initialized in `register_event`, or improper type casts will occur.
|
|
update: unsafe fn(MutUntyped),
|
|
}
|
|
|
|
/// A registry of all of the [`Events`] in the [`World`], used by [`event_update_system`](crate::event::update::event_update_system)
|
|
/// to update all of the events.
|
|
#[derive(Resource, Default)]
|
|
pub struct EventRegistry {
|
|
/// Should the events be updated?
|
|
///
|
|
/// This field is generally automatically updated by the [`signal_event_update_system`](crate::event::update::signal_event_update_system).
|
|
pub should_update: ShouldUpdateEvents,
|
|
event_updates: Vec<RegisteredEvent>,
|
|
}
|
|
|
|
/// Controls whether or not the events in an [`EventRegistry`] should be updated.
|
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum ShouldUpdateEvents {
|
|
/// Without any fixed timestep, events should always be updated each frame.
|
|
#[default]
|
|
Always,
|
|
/// We need to wait until at least one pass of the fixed update schedules to update the events.
|
|
Waiting,
|
|
/// At least one pass of the fixed update schedules has occurred, and the events are ready to be updated.
|
|
Ready,
|
|
}
|
|
|
|
impl EventRegistry {
|
|
/// Registers an event type to be updated in a given [`World`]
|
|
///
|
|
/// If no instance of the [`EventRegistry`] exists in the world, this will add one - otherwise it will use
|
|
/// the existing instance.
|
|
pub fn register_event<T: Event>(world: &mut World) {
|
|
// By initializing the resource here, we can be sure that it is present,
|
|
// and receive the correct, up-to-date `ComponentId` even if it was previously removed.
|
|
let component_id = world.init_resource::<Events<T>>();
|
|
let mut registry = world.get_resource_or_init::<Self>();
|
|
registry.event_updates.push(RegisteredEvent {
|
|
component_id,
|
|
previously_updated: false,
|
|
update: |ptr| {
|
|
// SAFETY: The resource was initialized with the type Events<T>.
|
|
unsafe { ptr.with_type::<Events<T>>() }
|
|
.bypass_change_detection()
|
|
.update();
|
|
},
|
|
});
|
|
}
|
|
|
|
/// Updates all of the registered events in the World.
|
|
pub fn run_updates(&mut self, world: &mut World, last_change_tick: Tick) {
|
|
for registered_event in &mut self.event_updates {
|
|
// Bypass the type ID -> Component ID lookup with the cached component ID.
|
|
if let Some(events) = world.get_resource_mut_by_id(registered_event.component_id) {
|
|
let has_changed = events.has_changed_since(last_change_tick);
|
|
if registered_event.previously_updated || has_changed {
|
|
// SAFETY: The update function pointer is called with the resource
|
|
// fetched from the same component ID.
|
|
unsafe { (registered_event.update)(events) };
|
|
// Always set to true if the events have changed, otherwise disable running on the second invocation
|
|
// to wait for more changes.
|
|
registered_event.previously_updated =
|
|
has_changed || !registered_event.previously_updated;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Removes an event from the world and it's associated [`EventRegistry`].
|
|
pub fn deregister_events<T: Event>(world: &mut World) {
|
|
let component_id = world.init_resource::<Events<T>>();
|
|
let mut registry = world.get_resource_or_init::<Self>();
|
|
registry
|
|
.event_updates
|
|
.retain(|e| e.component_id != component_id);
|
|
world.remove_resource::<Events<T>>();
|
|
}
|
|
}
|