bevy_ecs/message/message_registry.rs
1use crate::{
2 change_detection::{DetectChangesMut, MutUntyped},
3 component::{ComponentId, Tick},
4 message::{Message, Messages},
5 resource::Resource,
6 world::World,
7};
8use alloc::vec::Vec;
9
10#[doc(hidden)]
11struct RegisteredMessage {
12 messages_component: ComponentId,
13 // Required to flush the secondary buffer and drop messages even if left unchanged.
14 previously_updated: bool,
15 // SAFETY: The message's component ID and the function must be used to fetch the Messages<T> resource
16 // of the same type initialized in `register_message`, or improper type casts will occur.
17 update: unsafe fn(MutUntyped),
18}
19
20/// A registry of all of the [`Messages`] in the [`World`], used by [`message_update_system`](crate::message::message_update_system)
21/// to update all of the messages.
22#[derive(Resource, Default)]
23pub struct MessageRegistry {
24 /// Should the messages be updated?
25 ///
26 /// This field is generally automatically updated by the [`signal_message_update_system`](crate::message::signal_message_update_system).
27 pub should_update: ShouldUpdateMessages,
28 message_updates: Vec<RegisteredMessage>,
29}
30
31/// Controls whether or not the messages in an [`MessageRegistry`] should be updated.
32#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ShouldUpdateMessages {
34 /// Without any fixed timestep, messages should always be updated each frame.
35 #[default]
36 Always,
37 /// We need to wait until at least one pass of the fixed update schedules to update the messages.
38 Waiting,
39 /// At least one pass of the fixed update schedules has occurred, and the messages are ready to be updated.
40 Ready,
41}
42
43impl MessageRegistry {
44 /// Registers a message type to be updated in a given [`World`]
45 ///
46 /// If no instance of the [`MessageRegistry`] exists in the world, this will add one - otherwise it will use
47 /// the existing instance.
48 pub fn register_message<T: Message>(world: &mut World) {
49 // By initializing the resource here, we can be sure that it is present,
50 // and receive the correct, up-to-date `ComponentId` even if it was previously removed.
51 let component_id = world.init_resource::<Messages<T>>();
52 let mut registry = world.get_resource_or_init::<Self>();
53 registry.message_updates.push(RegisteredMessage {
54 messages_component: component_id,
55 previously_updated: false,
56 update: |ptr| {
57 // SAFETY: The resource was initialized with the type Messages<T>.
58 unsafe { ptr.with_type::<Messages<T>>() }
59 .bypass_change_detection()
60 .update();
61 },
62 });
63 }
64
65 /// Updates all of the registered messages in the World.
66 pub fn run_updates(&mut self, world: &mut World, last_change_tick: Tick) {
67 for registered_message in &mut self.message_updates {
68 // Bypass the type ID -> Component ID lookup with the cached component ID.
69 if let Some(messages) =
70 world.get_resource_mut_by_id(registered_message.messages_component)
71 {
72 let has_changed = messages.has_changed_since(last_change_tick);
73 if registered_message.previously_updated || has_changed {
74 // SAFETY: The update function pointer is called with the resource
75 // fetched from the same component ID.
76 unsafe { (registered_message.update)(messages) };
77 // Always set to true if the messages have changed, otherwise disable running on the second invocation
78 // to wait for more changes.
79 registered_message.previously_updated =
80 has_changed || !registered_message.previously_updated;
81 }
82 }
83 }
84 }
85
86 /// Removes a message from the world and its associated [`MessageRegistry`].
87 pub fn deregister_messages<T: Message>(world: &mut World) {
88 let component_id = world.init_resource::<Messages<T>>();
89 let mut registry = world.get_resource_or_init::<Self>();
90 registry
91 .message_updates
92 .retain(|e| e.messages_component != component_id);
93 world.remove_resource::<Messages<T>>();
94 }
95}