Skip to main content

naia_shared/world/update/
global_diff_handler.rs

1use std::{collections::HashMap, net::SocketAddr};
2
3use crate::{ComponentKind, ComponentKinds, GlobalEntity, GlobalWorldManagerType};
4
5use crate::world::update::mut_channel::{MutChannel, MutReceiver, MutReceiverBuilder, MutSender};
6
7/// Global registry of mutation channels for every (entity, component) pair, used to fan out property changes to per-user dirty queues.
8pub struct GlobalDiffHandler {
9    mut_receiver_builders: HashMap<(GlobalEntity, ComponentKind), MutReceiverBuilder>,
10    /// `ComponentKind` → NetId (== bit position in the per-user
11    /// `DirtyQueue`'s flat-strided bitset). Populated lazily as
12    /// `register_component` fires; `UserDiffHandler` reads this to
13    /// wire `DirtyNotifier`s with a precomputed `kind_bit`.
14    /// Phase 9.4 / Stage E. Widened to `u16` 2026-05-05 with the
15    /// unlimited-kind-count refactor (was `u8`, capped at 256 by the
16    /// type even though the assertion was 64).
17    kind_bits: HashMap<ComponentKind, u16>,
18    /// Maximum NetId observed at registration. Used to derive the
19    /// `DirtyQueue` stride at `UserDiffHandler::new` (one stride for
20    /// the lifetime of the handler — protocol locks before any
21    /// handler is created).
22    max_kind_count: u16,
23}
24
25#[cfg(feature = "test_utils")]
26impl GlobalDiffHandler {
27    #[doc(hidden)]
28    pub fn receiver_count(&self) -> usize {
29        self.mut_receiver_builders.len()
30    }
31
32    #[doc(hidden)]
33    pub fn receiver_count_by_kind(&self) -> HashMap<ComponentKind, usize> {
34        let mut map = HashMap::new();
35        for &(_, kind) in self.mut_receiver_builders.keys() {
36            *map.entry(kind).or_insert(0) += 1;
37        }
38        map
39    }
40}
41
42impl Default for GlobalDiffHandler {
43    fn default() -> Self {
44        Self::new()
45    }
46}
47
48impl GlobalDiffHandler {
49    /// Creates an empty `GlobalDiffHandler`.
50    pub fn new() -> Self {
51        Self {
52            mut_receiver_builders: HashMap::new(),
53            kind_bits: HashMap::new(),
54            max_kind_count: 0,
55        }
56    }
57
58    /// NetId of a registered kind, used as bit position in the per-user
59    /// `DirtyQueue`'s flat-strided bitset. Returns `None` if the kind
60    /// has never gone through `register_component` here.
61    pub fn kind_bit(&self, component_kind: &ComponentKind) -> Option<u16> {
62        self.kind_bits.get(component_kind).copied()
63    }
64
65    /// Highest `kind_bit + 1` ever registered with this handler. The
66    /// per-user `DirtyQueue` uses this to size its stride
67    /// (`ceil(kind_count / 64)` `AtomicU64` words per entity).
68    pub fn kind_count(&self) -> u16 {
69        self.max_kind_count
70    }
71
72    /// Returns `true` if a mutation channel is registered for `(global_entity, component_kind)`.
73    pub fn has_component(&self, global_entity: &GlobalEntity, component_kind: &ComponentKind) -> bool {
74        self.mut_receiver_builders.contains_key(&(*global_entity, *component_kind))
75    }
76
77    /// Creates a `MutSender`/`MutReceiverBuilder` pair for `(global_entity, component_kind)` and returns the sender.
78    pub fn register_component(
79        &mut self,
80        component_kinds: &ComponentKinds,
81        global_world_manager: &dyn GlobalWorldManagerType,
82        global_entity: &GlobalEntity,
83        component_kind: &ComponentKind,
84        diff_mask_length: u8,
85    ) -> MutSender {
86        let name = component_kinds.kind_to_name(component_kind);
87
88        if self
89            .mut_receiver_builders
90            .contains_key(&(*global_entity, *component_kind))
91        {
92            panic!(
93                "GlobalDiffHandler: For Entity {:?}, Component {} cannot Register more than once!",
94                global_entity, name
95            );
96        }
97
98        let (sender, builder) = MutChannel::new_channel(global_world_manager, diff_mask_length);
99
100        self.mut_receiver_builders
101            .insert((*global_entity, *component_kind), builder);
102
103        if let std::collections::hash_map::Entry::Vacant(entry) =
104            self.kind_bits.entry(*component_kind)
105        {
106            if let Some(net_id) = component_kinds.net_id_of(component_kind) {
107                entry.insert(net_id);
108                if net_id + 1 > self.max_kind_count {
109                    self.max_kind_count = net_id + 1;
110                }
111            }
112        }
113
114        sender
115    }
116
117    /// Removes the mutation channel for `(entity, component_kind)`, stopping further dirty notifications.
118    pub fn deregister_component(&mut self, entity: &GlobalEntity, component_kind: &ComponentKind) {
119        self.mut_receiver_builders
120            .remove(&(*entity, *component_kind));
121    }
122
123    /// Builds a `MutReceiver` for `address` from the builder registered for `(entity, component_kind)`, if one exists.
124    pub fn receiver(
125        &self,
126        address: &Option<SocketAddr>,
127        entity: &GlobalEntity,
128        component_kind: &ComponentKind,
129    ) -> Option<MutReceiver> {
130        if let Some(builder) = self.mut_receiver_builders.get(&(*entity, *component_kind)) {
131            return builder.build(address);
132        }
133        None
134    }
135}