Skip to main content

naia_shared/world/entity/
entity_converters.rs

1use std::{
2    hash::Hash,
3    sync::{Arc, RwLock},
4};
5
6use crate::world::local::local_entity::{HostEntity, OwnedLocalEntity, RemoteEntity};
7use crate::world::update::mut_channel::MutChannelType;
8use crate::{
9    bigmap::BigMapKey,
10    world::{
11        delegation::auth_channel::EntityAuthAccessor,
12        entity::{error::EntityDoesNotExistError, global_entity::GlobalEntity},
13    },
14    ComponentKind, ComponentKinds, GlobalDiffHandler, HostEntityGenerator, InScopeEntities,
15    LocalEntityMap, PropertyMutator,
16};
17
18/// Global world state queries needed during message and component serialization.
19pub trait GlobalWorldManagerType: InScopeEntities<GlobalEntity> {
20    /// Returns the list of component kinds currently attached to `entity`, or `None` if the entity is not known.
21    fn component_kinds(&self, entity: &GlobalEntity) -> Option<Vec<ComponentKind>>;
22    /// Whether or not a given user can receive a Message/Component with an EntityProperty relating to the given Entity
23    fn entity_can_relate_to_user(&self, global_entity: &GlobalEntity, user_key: &u64) -> bool;
24    /// Creates a new `MutChannelType` of `diff_mask_length` bytes for a component's mutation tracking.
25    fn new_mut_channel(&self, diff_mask_length: u8) -> Arc<RwLock<dyn MutChannelType>>;
26    /// Returns a handle to the global diff handler used to fan out property mutations.
27    fn diff_handler(&self) -> Arc<RwLock<GlobalDiffHandler>>;
28    /// Registers a component for mutation tracking, returning a [`PropertyMutator`] wired to the global diff handler.
29    fn register_component(
30        &self,
31        component_kinds: &ComponentKinds,
32        global_entity: &GlobalEntity,
33        component_kind: &ComponentKind,
34        diff_mask_length: u8,
35    ) -> PropertyMutator;
36    /// Returns an [`EntityAuthAccessor`] for reading the delegation authority state of `global_entity`.
37    fn get_entity_auth_accessor(&self, global_entity: &GlobalEntity) -> EntityAuthAccessor;
38    /// Returns `true` if `global_entity` requires a `PropertyMutator` to notify authority changes during delegation.
39    fn entity_needs_mutator_for_delegation(&self, global_entity: &GlobalEntity) -> bool;
40    /// Returns `true` if `global_entity` is actively being replicated.
41    fn entity_is_replicating(&self, global_entity: &GlobalEntity) -> bool;
42    /// Returns `true` if `global_entity` was spawned as a static entity.
43    fn entity_is_static(&self, global_entity: &GlobalEntity) -> bool;
44}
45
46/// Bidirectional conversion between a world-type entity `E` and a `GlobalEntity`.
47pub trait EntityAndGlobalEntityConverter<E: Copy + Eq + Hash + Sync + Send> {
48    /// Resolves `global_entity` to the corresponding world-local entity `E`, or returns an error if not found.
49    fn global_entity_to_entity(
50        &self,
51        global_entity: &GlobalEntity,
52    ) -> Result<E, EntityDoesNotExistError>;
53    /// Resolves a world-local `entity` to its stable [`GlobalEntity`] identifier, or returns an error if not found.
54    fn entity_to_global_entity(&self, entity: &E) -> Result<GlobalEntity, EntityDoesNotExistError>;
55}
56
57/// Conversions between the connection-local host/remote entity representations and the global entity space.
58pub trait LocalEntityAndGlobalEntityConverter {
59    /// Returns the [`HostEntity`] for `global_entity` if one is registered, or an error otherwise.
60    fn global_entity_to_host_entity(
61        &self,
62        global_entity: &GlobalEntity,
63    ) -> Result<HostEntity, EntityDoesNotExistError>;
64    /// Returns the [`RemoteEntity`] for `global_entity` if one is registered, or an error otherwise.
65    fn global_entity_to_remote_entity(
66        &self,
67        global_entity: &GlobalEntity,
68    ) -> Result<RemoteEntity, EntityDoesNotExistError>;
69    /// Returns the [`OwnedLocalEntity`] (host or remote) for `global_entity`, or an error if not found.
70    fn global_entity_to_owned_entity(
71        &self,
72        global_entity: &GlobalEntity,
73    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError>;
74    /// Returns the [`GlobalEntity`] for a dynamic `host_entity`, or an error if not found.
75    fn host_entity_to_global_entity(
76        &self,
77        host_entity: &HostEntity,
78    ) -> Result<GlobalEntity, EntityDoesNotExistError>;
79    /// Returns the [`GlobalEntity`] for a static `host_entity`, or an error if not found.
80    fn static_host_entity_to_global_entity(
81        &self,
82        host_entity: &HostEntity,
83    ) -> Result<GlobalEntity, EntityDoesNotExistError>;
84    /// Returns the [`GlobalEntity`] for `remote_entity`, or an error if not found.
85    fn remote_entity_to_global_entity(
86        &self,
87        remote_entity: &RemoteEntity,
88    ) -> Result<GlobalEntity, EntityDoesNotExistError>;
89    /// Returns the [`GlobalEntity`] for `owned_entity`, dispatching to the appropriate host or remote lookup.
90    fn owned_entity_to_global_entity(
91        &self,
92        owned_entity: &OwnedLocalEntity,
93    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
94        match owned_entity {
95            OwnedLocalEntity::Host { id, is_static: true } => {
96                self.static_host_entity_to_global_entity(&HostEntity::new(*id))
97            }
98            OwnedLocalEntity::Host { id, is_static: false } => {
99                self.host_entity_to_global_entity(&HostEntity::new(*id))
100            }
101            OwnedLocalEntity::Remote { id, is_static } => {
102                let remote = if *is_static { RemoteEntity::new_static(*id) } else { RemoteEntity::new(*id) };
103                self.remote_entity_to_global_entity(&remote)
104            }
105        }
106    }
107    /// Returns the current redirect target for `entity`, or `entity` unchanged if no redirect is installed.
108    fn apply_entity_redirect(&self, entity: &OwnedLocalEntity) -> OwnedLocalEntity;
109}
110
111/// No-op converter that always succeeds with entity ID 0; useful in test contexts where real mapping is not needed.
112pub struct FakeEntityConverter;
113
114impl LocalEntityAndGlobalEntityConverter for FakeEntityConverter {
115    fn global_entity_to_host_entity(
116        &self,
117        _: &GlobalEntity,
118    ) -> Result<HostEntity, EntityDoesNotExistError> {
119        Ok(HostEntity::new(0))
120    }
121
122    fn global_entity_to_remote_entity(
123        &self,
124        _: &GlobalEntity,
125    ) -> Result<RemoteEntity, EntityDoesNotExistError> {
126        Ok(RemoteEntity::new(0))
127    }
128
129    fn global_entity_to_owned_entity(
130        &self,
131        _global_entity: &GlobalEntity,
132    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError> {
133        Ok(OwnedLocalEntity::Host { id: 0, is_static: false })
134    }
135
136    fn host_entity_to_global_entity(
137        &self,
138        _: &HostEntity,
139    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
140        Ok(GlobalEntity::from_u64(0))
141    }
142
143    fn static_host_entity_to_global_entity(
144        &self,
145        _: &HostEntity,
146    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
147        Ok(GlobalEntity::from_u64(0))
148    }
149
150    fn remote_entity_to_global_entity(
151        &self,
152        _: &RemoteEntity,
153    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
154        Ok(GlobalEntity::from_u64(0))
155    }
156
157    fn apply_entity_redirect(&self, entity: &OwnedLocalEntity) -> OwnedLocalEntity {
158        *entity // No redirects in fake converter
159    }
160}
161
162impl LocalEntityAndGlobalEntityConverterMut for FakeEntityConverter {
163    fn get_or_reserve_entity(
164        &mut self,
165        _global_entity: &GlobalEntity,
166    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError> {
167        Ok(OwnedLocalEntity::Host { id: 0, is_static: false })
168    }
169}
170
171/// Mutable extension of `LocalEntityAndGlobalEntityConverter` that can allocate new host-side entity slots.
172pub trait LocalEntityAndGlobalEntityConverterMut: LocalEntityAndGlobalEntityConverter {
173    /// Looks up the local entity for `global_entity`, reserving a new host slot if none exists yet.
174    fn get_or_reserve_entity(
175        &mut self,
176        global_entity: &GlobalEntity,
177    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError>;
178}
179
180/// Stateful converter used when writing messages: looks up or reserves host-side entity slots on demand.
181pub struct EntityConverterMut<'a, 'b> {
182    global_world_manager: &'a dyn GlobalWorldManagerType,
183    local_entity_map: &'b mut LocalEntityMap,
184    host_entity_generator: &'b mut HostEntityGenerator,
185}
186
187impl<'a, 'b> EntityConverterMut<'a, 'b> {
188    /// Creates an `EntityConverterMut` backed by the given world manager, entity map, and generator.
189    pub fn new(
190        global_world_manager: &'a dyn GlobalWorldManagerType,
191        local_entity_map: &'b mut LocalEntityMap,
192        host_entity_generator: &'b mut HostEntityGenerator,
193    ) -> Self {
194        Self {
195            global_world_manager,
196            local_entity_map,
197            host_entity_generator,
198        }
199    }
200}
201
202impl<'a, 'b> LocalEntityAndGlobalEntityConverter for EntityConverterMut<'a, 'b> {
203    fn global_entity_to_host_entity(
204        &self,
205        global_entity: &GlobalEntity,
206    ) -> Result<HostEntity, EntityDoesNotExistError> {
207        self.local_entity_map
208            .entity_converter()
209            .global_entity_to_host_entity(global_entity)
210    }
211
212    fn global_entity_to_remote_entity(
213        &self,
214        global_entity: &GlobalEntity,
215    ) -> Result<RemoteEntity, EntityDoesNotExistError> {
216        self.local_entity_map
217            .entity_converter()
218            .global_entity_to_remote_entity(global_entity)
219    }
220
221    fn global_entity_to_owned_entity(
222        &self,
223        global_entity: &GlobalEntity,
224    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError> {
225        self.local_entity_map
226            .entity_converter()
227            .global_entity_to_owned_entity(global_entity)
228    }
229
230    fn host_entity_to_global_entity(
231        &self,
232        host_entity: &HostEntity,
233    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
234        self.local_entity_map
235            .entity_converter()
236            .host_entity_to_global_entity(host_entity)
237    }
238
239    fn static_host_entity_to_global_entity(
240        &self,
241        host_entity: &HostEntity,
242    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
243        self.local_entity_map
244            .entity_converter()
245            .static_host_entity_to_global_entity(host_entity)
246    }
247
248    fn remote_entity_to_global_entity(
249        &self,
250        remote_entity: &RemoteEntity,
251    ) -> Result<GlobalEntity, EntityDoesNotExistError> {
252        self.local_entity_map
253            .entity_converter()
254            .remote_entity_to_global_entity(remote_entity)
255    }
256
257    fn apply_entity_redirect(&self, entity: &OwnedLocalEntity) -> OwnedLocalEntity {
258        self.local_entity_map
259            .entity_converter()
260            .apply_entity_redirect(entity)
261    }
262}
263
264impl<'a, 'b> LocalEntityAndGlobalEntityConverterMut for EntityConverterMut<'a, 'b> {
265    fn get_or_reserve_entity(
266        &mut self,
267        global_entity: &GlobalEntity,
268    ) -> Result<OwnedLocalEntity, EntityDoesNotExistError> {
269        if !self
270            .global_world_manager
271            .entity_can_relate_to_user(global_entity, self.host_entity_generator.get_user_key())
272        {
273            return Err(EntityDoesNotExistError);
274        }
275        let result = self
276            .local_entity_map
277            .global_entity_to_owned_entity(global_entity);
278        if result.is_ok() {
279            // info!("get_or_reserve_entity(). `global_entity`: {:?} --> `owned_entity`: {:?}", global_entity, result);
280            return result;
281        }
282
283        let host_entity = self
284            .host_entity_generator
285            .host_reserve_entity(self.local_entity_map, global_entity);
286
287        // warn!("get_or_reserve_entity() `global_entity` {:?} is not owned by user, attempting to reserve. `host_entity`: {:?}", global_entity, host_entity);
288
289        Ok(host_entity.copy_to_owned())
290    }
291}