Skip to main content

naia_shared/world/host/
host_world_manager.rs

1use std::{
2    collections::HashMap,
3    hash::Hash,
4};
5
6use crate::{
7    messages::channels::receivers::reliable_receiver::ReliableReceiver,
8    world::{
9        sync::{HostEngine, HostEntityChannel, RemoteEngine, RemoteEntityChannel},
10        update::entity_update_manager::EntityUpdateManager,
11    },
12    ComponentKind, ComponentKinds, EntityCommand, EntityConverterMut, EntityEvent, EntityMessage,
13    EntityMessageReceiver, GlobalEntity, GlobalEntitySpawner, GlobalWorldManagerType, HostEntity,
14    HostEntityGenerator, HostType, LocalEntityAndGlobalEntityConverter, LocalEntityMap,
15    MessageIndex, ShortMessageIndex, WorldMutType,
16};
17
18/// Sequence number identifying a top-level entity command sent over the reliable channel.
19pub type CommandId = MessageIndex;
20/// Sequence number identifying a sub-command within a top-level entity command.
21pub type SubCommandId = ShortMessageIndex;
22
23/// Drives outbound entity-lifecycle replication for one side of a connection, tracking delivery state and processing inbound authority responses.
24pub struct HostWorldManager {
25    // host entity generator
26    entity_generator: HostEntityGenerator,
27
28    // For Server, this contains the Entities that the Server has authority over, that it syncs to the Client
29    // For Client, this contains the non-Delegated Entities that the Client has authority over, that it syncs to the Server
30    host_engine: HostEngine,
31
32    // For Server, this contains the Entities that the Server has authority over, that have been delivered to the Client
33    // For Client, this contains the non-Delegated Entities that the Client has authority over, that have been delivered to the Server
34    delivered_receiver: ReliableReceiver<EntityMessage<HostEntity>>,
35    delivered_engine: RemoteEngine<HostEntity>,
36    incoming_events: Vec<EntityEvent>,
37}
38
39impl HostWorldManager {
40    /// Creates a `HostWorldManager` for the given `host_type` side and `user_key`.
41    pub fn new(host_type: HostType, user_key: u64) -> Self {
42        Self {
43            entity_generator: HostEntityGenerator::new(user_key),
44            host_engine: HostEngine::new(host_type),
45            delivered_receiver: ReliableReceiver::new(),
46            delivered_engine: RemoteEngine::new(host_type.invert()),
47            incoming_events: Vec::new(),
48        }
49    }
50
51    pub(crate) fn entity_converter_mut<'a, 'b>(
52        &'b mut self,
53        global_world_manager: &'a dyn GlobalWorldManagerType,
54        entity_map: &'b mut LocalEntityMap,
55    ) -> EntityConverterMut<'a, 'b> {
56        EntityConverterMut::new(global_world_manager, entity_map, &mut self.entity_generator)
57    }
58
59    // Collect
60
61    /// Processes `incoming_messages` through the host engine and returns all resulting [`EntityEvent`]s.
62    pub fn take_incoming_events<E: Copy + Eq + Hash + Send + Sync, W: WorldMutType<E>>(
63        &mut self,
64        spawner: &mut dyn GlobalEntitySpawner<E>,
65        global_world_manager: &dyn GlobalWorldManagerType,
66        local_entity_map: &LocalEntityMap,
67        world: &mut W,
68        incoming_messages: Vec<(MessageIndex, EntityMessage<HostEntity>)>,
69    ) -> Vec<EntityEvent> {
70        let incoming_messages = EntityMessageReceiver::host_take_incoming_events(
71            &mut self.host_engine,
72            incoming_messages,
73        );
74
75        self.process_incoming_messages(
76            spawner,
77            global_world_manager,
78            local_entity_map,
79            world,
80            incoming_messages,
81        );
82
83        std::mem::take(&mut self.incoming_events)
84    }
85
86    /// Drains and returns all pending outbound [`EntityCommand`]s queued by the host engine.
87    pub fn take_outgoing_commands(&mut self) -> Vec<EntityCommand> {
88        self.host_engine.take_outgoing_commands()
89    }
90
91    pub(crate) fn host_generate_entity(&mut self) -> HostEntity {
92        self.entity_generator.generate_host_entity()
93    }
94
95    pub(crate) fn host_generate_static_entity(&mut self) -> HostEntity {
96        self.entity_generator.generate_static_host_entity()
97    }
98
99    /// Sends the initial spawn command(s) for a static entity, coalescing components into a single message when present.
100    pub fn init_static_entity_send_host_commands(
101        &mut self,
102        converter: &dyn LocalEntityAndGlobalEntityConverter,
103        global_entity: &GlobalEntity,
104        component_kinds: Vec<ComponentKind>,
105    ) {
106        // Static entities: NEVER register for diff-tracking — they don't change after spawn.
107        if !component_kinds.is_empty() {
108            self.host_engine.send_command(
109                converter,
110                EntityCommand::SpawnWithComponents(*global_entity, component_kinds),
111            );
112            return;
113        }
114        self.host_engine
115            .send_command(converter, EntityCommand::Spawn(*global_entity));
116    }
117
118    pub(crate) fn host_reserve_entity(
119        &mut self,
120        entity_map: &mut LocalEntityMap,
121        global_entity: &GlobalEntity,
122    ) -> HostEntity {
123        self.entity_generator
124            .host_reserve_entity(entity_map, global_entity)
125    }
126
127    pub(crate) fn host_removed_reserved_entity(
128        &mut self,
129        global_entity: &GlobalEntity,
130    ) -> Option<HostEntity> {
131        self.entity_generator
132            .host_remove_reserved_entity(global_entity)
133    }
134
135    pub(crate) fn has_entity(&self, host_entity: &HostEntity) -> bool {
136        self.get_host_world().contains_key(host_entity)
137    }
138
139    /// Registers components for diff-tracking and sends initial spawn command(s) when an entity first enters connection scope.
140    pub fn init_entity_send_host_commands(
141        &mut self,
142        converter: &dyn LocalEntityAndGlobalEntityConverter,
143        global_entity: &GlobalEntity,
144        component_kinds: Vec<ComponentKind>,
145        entity_update_manager: &mut EntityUpdateManager,
146        component_kinds_map: &ComponentKinds,
147    ) {
148        // Register only mutable components for diff-tracking immediately at scope entry.
149        // Immutable components (is_immutable == true) are never diff-tracked — skip them.
150        for component_kind in &component_kinds {
151            if !component_kinds_map.kind_is_immutable(component_kind) {
152                entity_update_manager.register_component(global_entity, component_kind);
153            }
154        }
155
156        if !component_kinds.is_empty() {
157            // Coalesce Spawn + N InsertComponent into one reliable message
158            self.host_engine.send_command(
159                converter,
160                EntityCommand::SpawnWithComponents(*global_entity, component_kinds),
161            );
162            return;
163        }
164
165        // Zero-component path: plain Spawn with no component payloads
166        self.host_engine
167            .send_command(converter, EntityCommand::Spawn(*global_entity));
168    }
169
170    /// Enqueues `command` for reliable delivery to the remote peer.
171    pub fn send_command(
172        &mut self,
173        converter: &dyn LocalEntityAndGlobalEntityConverter,
174        command: EntityCommand,
175    ) {
176        self.host_engine.send_command(converter, command);
177    }
178
179    pub(crate) fn get_host_world(&self) -> &HashMap<HostEntity, HostEntityChannel> {
180        self.host_engine.get_world()
181    }
182
183    pub(crate) fn extract_entity_commands(
184        &mut self,
185        host_entity: &HostEntity,
186    ) -> Vec<EntityCommand> {
187        self.host_engine.extract_entity_commands(host_entity)
188    }
189
190    pub(crate) fn get_delivered_world(&self) -> &HashMap<HostEntity, RemoteEntityChannel> {
191        self.delivered_engine.get_world()
192    }
193
194    pub(crate) fn is_component_updatable(
195        &self,
196        converter: &dyn LocalEntityAndGlobalEntityConverter,
197        global_entity: &GlobalEntity,
198        kind: &ComponentKind,
199    ) -> bool {
200        let Ok(host_entity) = converter.global_entity_to_host_entity(global_entity) else {
201            return false;
202        };
203        let Some(host_channel) = self.get_host_world().get(&host_entity) else {
204            return false;
205        };
206        if !host_channel.component_kinds().contains(kind) {
207            return false;
208        }
209        let Some(delivered_channel) = self.get_delivered_world().get(&host_entity) else {
210            return false;
211        };
212        delivered_channel.has_component_kind(kind)
213    }
214
215    pub(crate) fn deliver_message(
216        &mut self,
217        command_id: CommandId,
218        message: EntityMessage<HostEntity>,
219    ) {
220        self.delivered_receiver.buffer_message(command_id, message);
221    }
222
223    pub(crate) fn process_delivered_commands(
224        &mut self,
225        local_entity_map: &mut LocalEntityMap,
226        entity_update_manager: &mut EntityUpdateManager,
227    ) {
228        let delivered_messages: Vec<(MessageIndex, EntityMessage<HostEntity>)> =
229            self.delivered_receiver.receive_messages();
230
231        // Filter out MigrateResponse messages - they should not be processed by RemoteEngine
232        // MigrateResponse is a client-only message that the server tracks for delivery but doesn't process
233        let filtered_messages: Vec<(MessageIndex, EntityMessage<HostEntity>)> = delivered_messages
234            .into_iter()
235            .filter(|(_, msg)| !matches!(msg, EntityMessage::MigrateResponse(_, _, _)))
236            .collect();
237
238        for message in EntityMessageReceiver::remote_take_incoming_messages(
239            &mut self.delivered_engine,
240            filtered_messages,
241        ) {
242            match message {
243                EntityMessage::Spawn(host_entity) => {
244                    self.on_delivered_spawn_entity(&host_entity);
245                }
246                EntityMessage::Despawn(host_entity) => {
247                    self.on_delivered_despawn_entity(local_entity_map, &host_entity);
248                }
249                EntityMessage::InsertComponent(host_entity, component_kind) => {
250                    let Some(global_entity) =
251                        local_entity_map.global_entity_from_host(&host_entity)
252                    else {
253                        return;
254                    };
255                    self.on_delivered_insert_component(
256                        entity_update_manager,
257                        global_entity,
258                        &component_kind,
259                    );
260                }
261                EntityMessage::RemoveComponent(host_entity, component_kind) => {
262                    let Some(global_entity) =
263                        local_entity_map.global_entity_from_host(&host_entity)
264                    else {
265                        return;
266                    };
267                    self.on_delivered_remove_component(
268                        entity_update_manager,
269                        global_entity,
270                        &component_kind,
271                    );
272                }
273                EntityMessage::Noop => {
274                    // do nothing
275                }
276                _ => {
277                    // Only Auth-related messages are left here
278                    // Right now it doesn't seem like we need to track auth state here
279                }
280            }
281        }
282    }
283
284    fn process_incoming_messages<E: Copy + Eq + Hash + Send + Sync, W: WorldMutType<E>>(
285        &mut self,
286        _spawner: &mut dyn GlobalEntitySpawner<E>,
287        _global_world_manager: &dyn GlobalWorldManagerType,
288        local_entity_map: &LocalEntityMap,
289        _world: &mut W,
290        incoming_messages: Vec<EntityMessage<HostEntity>>,
291    ) {
292        // execute the action and emit an event
293        for message in incoming_messages {
294            match message {
295                // These variants are sent server→client for remote-owned entities, routed through
296                // RemoteWorldManager, not HostWorldManager. A HostWorldManager processes messages
297                // about client-created (host-owned) entities only; the server never sends these
298                // variants back to the originating host.
299                EntityMessage::Spawn(_) => {
300                    unreachable!("Server never sends Spawn to the originating HostWorldManager");
301                }
302                EntityMessage::Despawn(_) => {
303                    unreachable!("Server never sends Despawn to the originating HostWorldManager");
304                }
305                EntityMessage::InsertComponent(_, _) => {
306                    unreachable!("Server never sends InsertComponent to the originating HostWorldManager");
307                }
308                EntityMessage::RemoveComponent(_, _) => {
309                    unreachable!("Server never sends RemoveComponent to the originating HostWorldManager");
310                }
311                EntityMessage::Publish(_, _) => {
312                    unreachable!("Server never sends Publish to the originating HostWorldManager");
313                }
314                EntityMessage::Unpublish(_, _) => {
315                    unreachable!("Server never sends Unpublish to the originating HostWorldManager");
316                }
317                EntityMessage::EnableDelegation(_, _) => {
318                    unreachable!("Server never sends EnableDelegation to the originating HostWorldManager");
319                }
320                EntityMessage::DisableDelegation(_, _) => {
321                    unreachable!("Server never sends DisableDelegation to the originating HostWorldManager");
322                }
323                EntityMessage::SetAuthority(_, _, _) => {
324                    unreachable!("Server never sends SetAuthority to the originating HostWorldManager");
325                }
326                EntityMessage::MigrateResponse(_sub_id, client_host_entity, new_remote_entity) => {
327                    // Client receives MigrateResponse from server telling it to migrate
328                    // a client-created delegated entity from HostEntity to RemoteEntity
329
330                    // Look up the global entity from the client's HostEntity
331                    let global_entity = *local_entity_map.global_entity_from_host(&client_host_entity)
332                        .expect("Host entity not found in local entity map during MigrateResponse processing");
333
334                    // Create event for the client to process the migration
335                    self.incoming_events.push(EntityEvent::MigrateResponse(
336                        global_entity,
337                        new_remote_entity,
338                    ));
339                }
340                EntityMessage::Noop => {
341                    // do nothing
342                }
343                // Whitelisted incoming messages:
344                // 1. EntityMessage::EnableDelegationResponse
345                // 2. EntityMessage::RequestAuthority
346                // 3. EntityMessage::ReleaseAuthority
347                msg => {
348                    if let Some(event) = msg.to_event(local_entity_map) {
349                        self.incoming_events.push(event);
350                    }
351                }
352            }
353        }
354    }
355
356    fn on_delivered_spawn_entity(&mut self, _host_entity: &HostEntity) {
357        #[cfg(feature = "observability")]
358        metrics::counter!(crate::SERVER_SPAWNS_TOTAL).increment(1);
359    }
360
361    /// Handles confirmed delivery of a despawn command, recycling the host entity ID and updating metrics.
362    pub fn on_delivered_despawn_entity(
363        &mut self,
364        local_entity_map: &mut LocalEntityMap,
365        host_entity: &HostEntity,
366    ) {
367        #[cfg(feature = "observability")]
368        metrics::counter!(crate::SERVER_DESPAWNS_TOTAL).increment(1);
369        self.entity_generator
370            .remove_by_host_entity(local_entity_map, host_entity);
371    }
372
373    fn on_delivered_insert_component(
374        &mut self,
375        _entity_update_manager: &mut EntityUpdateManager,
376        _global_entity: &GlobalEntity,
377        _component_kind: &ComponentKind,
378    ) {
379        // Component is already registered when entity comes into scope (in host_init_entity),
380        // so we don't need to register again here when InsertComponent is delivered
381        #[cfg(feature = "observability")]
382        metrics::counter!(crate::SERVER_COMPONENT_INSERTS_TOTAL).increment(1);
383    }
384
385    fn on_delivered_remove_component(
386        &mut self,
387        entity_update_manager: &mut EntityUpdateManager,
388        global_entity: &GlobalEntity,
389        component_kind: &ComponentKind,
390    ) {
391        #[cfg(feature = "observability")]
392        metrics::counter!(crate::SERVER_COMPONENT_REMOVES_TOTAL).increment(1);
393        entity_update_manager.deregister_component(global_entity, component_kind);
394    }
395
396    pub(crate) fn insert_entity_channel(&mut self, entity: HostEntity, channel: HostEntityChannel) {
397        self.host_engine.insert_entity_channel(entity, channel);
398    }
399
400    pub(crate) fn get_entity_channel(&self, entity: &HostEntity) -> Option<&HostEntityChannel> {
401        self.host_engine.get_entity_channel(entity)
402    }
403
404    pub(crate) fn get_entity_channel_mut(
405        &mut self,
406        entity: &HostEntity,
407    ) -> Option<&mut HostEntityChannel> {
408        self.host_engine.get_entity_channel_mut(entity)
409    }
410
411    pub(crate) fn remove_entity_channel(&mut self, entity: &HostEntity) -> HostEntityChannel {
412        self.host_engine.remove_entity_channel(entity)
413    }
414
415}
416// NOTE: on_delivered_migrate_response was removed (2026-05-10). The entity migration path
417// requires RemoteWorldManager drain/extract/despawn APIs that do not exist. Any future
418// implementation must correctly extract component_kinds and host_type from the remote channel
419// before constructing the new HostEntityChannel — the prior stub silently passed wrong values.