Skip to main content

naia_shared/world/entity/
entity_message.rs

1use log::error;
2
3use crate::{
4    world::component::component_kinds::ComponentKind,
5    world::host::host_world_manager::SubCommandId, EntityAuthStatus, EntityEvent,
6    EntityMessageType, HostEntity, LocalEntityMap, RemoteEntity,
7};
8
9/// Per-entity ordered wire message carrying a single entity lifecycle or authority event.
10#[derive(PartialEq, Eq, Debug, Clone)]
11pub enum EntityMessage<E: Copy + Eq + PartialEq> {
12    /// Spawn entity `E` with no initial components.
13    Spawn(E),
14    /// Spawn entity `E` pre-loaded with the listed component kinds.
15    SpawnWithComponents(E, Vec<ComponentKind>),
16    /// Despawn entity `E`.
17    Despawn(E),
18    /// Insert a component onto entity `E`.
19    InsertComponent(E, ComponentKind),
20    /// Remove a component from entity `E`.
21    RemoveComponent(E, ComponentKind),
22    /// Publish entity `E` (make it visible to other users).
23    Publish(SubCommandId, E),
24    /// Retract the publication of entity `E`.
25    Unpublish(SubCommandId, E),
26    /// Enable authority delegation for entity `E`.
27    EnableDelegation(SubCommandId, E),
28    /// Revoke authority delegation for entity `E` (server only).
29    DisableDelegation(SubCommandId, E),
30    /// Update authority status for entity `E` (server only).
31    SetAuthority(SubCommandId, E, EntityAuthStatus),
32
33    /// Client requests authority over entity `E`.
34    RequestAuthority(SubCommandId, E),
35    /// Client releases authority over entity `E`.
36    ReleaseAuthority(SubCommandId, E),
37    /// Client acknowledges that delegation has been enabled for entity `E`.
38    EnableDelegationResponse(SubCommandId, E),
39    /// Entity `E` has migrated; carries the old remote entity for reconciliation.
40    MigrateResponse(SubCommandId, E, RemoteEntity),
41
42    /// Placeholder that carries no payload.
43    Noop,
44}
45
46impl<E: Copy + Eq + PartialEq> EntityMessage<E> {
47    /// Returns the entity carried by this message, or `None` for `Noop`.
48    pub fn entity(&self) -> Option<E> {
49        match self {
50            Self::Spawn(entity) => Some(*entity),
51            Self::SpawnWithComponents(entity, _) => Some(*entity),
52            Self::Despawn(entity) => Some(*entity),
53            Self::InsertComponent(entity, _) => Some(*entity),
54            Self::RemoveComponent(entity, _) => Some(*entity),
55            Self::Publish(_, entity) => Some(*entity),
56            Self::Unpublish(_, entity) => Some(*entity),
57            Self::EnableDelegation(_, entity) => Some(*entity),
58            Self::EnableDelegationResponse(_, entity) => Some(*entity),
59            Self::DisableDelegation(_, entity) => Some(*entity),
60            Self::RequestAuthority(_, entity) => Some(*entity),
61            Self::ReleaseAuthority(_, entity) => Some(*entity),
62            Self::SetAuthority(_, entity, _) => Some(*entity),
63            Self::MigrateResponse(_, entity, _) => Some(*entity),
64            Self::Noop => None,
65        }
66    }
67
68    /// Returns `true` if this is a `Noop` message.
69    pub fn is_noop(&self) -> bool {
70        matches!(self, Self::Noop)
71    }
72
73    /// Returns the `ComponentKind` for insert/remove messages, or `None` otherwise.
74    pub fn component_kind(&self) -> Option<ComponentKind> {
75        match self {
76            Self::InsertComponent(_, component_kind) => Some(*component_kind),
77            Self::RemoveComponent(_, component_kind) => Some(*component_kind),
78            _ => None,
79        }
80    }
81
82    /// Returns a copy of this message with the entity replaced by `()`, preserving all other fields.
83    pub fn strip_entity(self) -> EntityMessage<()> {
84        match self {
85            Self::Spawn(_) => EntityMessage::Spawn(()),
86            Self::SpawnWithComponents(_, kinds) => EntityMessage::SpawnWithComponents((), kinds),
87            Self::Despawn(_) => EntityMessage::Despawn(()),
88            Self::InsertComponent(_, component_kind) => {
89                EntityMessage::InsertComponent((), component_kind)
90            }
91            Self::RemoveComponent(_, component_kind) => {
92                EntityMessage::RemoveComponent((), component_kind)
93            }
94            Self::Publish(sub_id, _) => EntityMessage::Publish(sub_id, ()),
95            Self::Unpublish(sub_id, _) => EntityMessage::Unpublish(sub_id, ()),
96            Self::EnableDelegation(sub_id, _) => EntityMessage::EnableDelegation(sub_id, ()),
97            Self::EnableDelegationResponse(sub_id, _) => {
98                EntityMessage::EnableDelegationResponse(sub_id, ())
99            }
100            Self::DisableDelegation(sub_id, _) => EntityMessage::DisableDelegation(sub_id, ()),
101            Self::RequestAuthority(sub_id, _) => EntityMessage::RequestAuthority(sub_id, ()),
102            Self::ReleaseAuthority(sub_id, _) => EntityMessage::ReleaseAuthority(sub_id, ()),
103            Self::SetAuthority(sub_id, _, status) => {
104                EntityMessage::SetAuthority(sub_id, (), status)
105            }
106            Self::MigrateResponse(sub_id, _, other_entity) => {
107                EntityMessage::MigrateResponse(sub_id, (), other_entity)
108            }
109            Self::Noop => panic!("Cannot strip entity from a Noop message"),
110        }
111    }
112
113    /// Returns this message re-typed with `entity` replacing the original entity field.
114    pub fn with_entity<O: Copy + Eq + PartialEq>(self, entity: O) -> EntityMessage<O> {
115        match self {
116            EntityMessage::Spawn(_) => EntityMessage::Spawn(entity),
117            EntityMessage::SpawnWithComponents(_, kinds) => EntityMessage::SpawnWithComponents(entity, kinds),
118            EntityMessage::Despawn(_) => EntityMessage::Despawn(entity),
119            EntityMessage::InsertComponent(_, component_kind) => {
120                EntityMessage::InsertComponent(entity, component_kind)
121            }
122            EntityMessage::RemoveComponent(_, component_kind) => {
123                EntityMessage::RemoveComponent(entity, component_kind)
124            }
125            EntityMessage::Publish(sub_id, _) => EntityMessage::Publish(sub_id, entity),
126            EntityMessage::Unpublish(sub_id, _) => EntityMessage::Unpublish(sub_id, entity),
127            EntityMessage::EnableDelegation(sub_id, _) => {
128                EntityMessage::EnableDelegation(sub_id, entity)
129            }
130            EntityMessage::EnableDelegationResponse(sub_id, _) => {
131                EntityMessage::EnableDelegationResponse(sub_id, entity)
132            }
133            EntityMessage::DisableDelegation(sub_id, _) => {
134                EntityMessage::DisableDelegation(sub_id, entity)
135            }
136            EntityMessage::RequestAuthority(sub_id, _) => {
137                EntityMessage::RequestAuthority(sub_id, entity)
138            }
139            EntityMessage::ReleaseAuthority(sub_id, _) => {
140                EntityMessage::ReleaseAuthority(sub_id, entity)
141            }
142            EntityMessage::SetAuthority(sub_id, _, status) => {
143                EntityMessage::SetAuthority(sub_id, entity, status)
144            }
145            EntityMessage::MigrateResponse(sub_id, _, other_entity) => {
146                EntityMessage::MigrateResponse(sub_id, entity, other_entity)
147            }
148            EntityMessage::Noop => panic!("Cannot add entity to a Noop message"),
149        }
150    }
151
152    /// Returns the `EntityMessageType` discriminant for this message.
153    pub fn get_type(&self) -> EntityMessageType {
154        match self {
155            Self::Spawn(_) => EntityMessageType::Spawn,
156            Self::SpawnWithComponents(_, _) => EntityMessageType::SpawnWithComponents,
157            Self::Despawn(_) => EntityMessageType::Despawn,
158            Self::InsertComponent(_, _) => EntityMessageType::InsertComponent,
159            Self::RemoveComponent(_, _) => EntityMessageType::RemoveComponent,
160            Self::Publish(_, _) => EntityMessageType::Publish,
161            Self::Unpublish(_, _) => EntityMessageType::Unpublish,
162            Self::EnableDelegation(_, _) => EntityMessageType::EnableDelegation,
163            Self::EnableDelegationResponse(_, _) => EntityMessageType::EnableDelegationResponse,
164            Self::DisableDelegation(_, _) => EntityMessageType::DisableDelegation,
165            Self::RequestAuthority(_, _) => EntityMessageType::RequestAuthority,
166            Self::ReleaseAuthority(_, _) => EntityMessageType::ReleaseAuthority,
167            Self::SetAuthority(_, _, _) => EntityMessageType::SetAuthority,
168            Self::MigrateResponse(_, _, _) => EntityMessageType::MigrateResponse,
169            Self::Noop => EntityMessageType::Noop,
170        }
171    }
172
173    /// Returns the `SubCommandId` for messages that carry one, or `None` for plain commands.
174    pub fn subcommand_id(&self) -> Option<SubCommandId> {
175        match self {
176            Self::Publish(sub_id, _) => Some(*sub_id),
177            Self::Unpublish(sub_id, _) => Some(*sub_id),
178            Self::EnableDelegation(sub_id, _) => Some(*sub_id),
179            Self::EnableDelegationResponse(sub_id, _) => Some(*sub_id),
180            Self::DisableDelegation(sub_id, _) => Some(*sub_id),
181            Self::RequestAuthority(sub_id, _) => Some(*sub_id),
182            Self::ReleaseAuthority(sub_id, _) => Some(*sub_id),
183            Self::SetAuthority(sub_id, _, _) => Some(*sub_id),
184            Self::MigrateResponse(sub_id, _, _) => Some(*sub_id),
185            _ => None,
186        }
187    }
188
189    /// Returns `self` with `new_entity` substituted wherever `old_entity` matches the message's entity field.
190    pub fn apply_entity_redirect<O: Copy + Eq + PartialEq>(
191        self,
192        old_entity: &E,
193        new_entity: &O,
194    ) -> EntityMessage<O> {
195        if let Some(entity) = self.entity() {
196            if entity == *old_entity {
197                return self.with_entity(*new_entity);
198            }
199        }
200        // If no entity or entity doesn't match, return a message with the new entity
201        self.with_entity(*new_entity)
202    }
203}
204//
205impl EntityMessage<RemoteEntity> {
206    //
207    //     pub fn to_host_message(self) -> EntityMessage<HostEntity> {
208    //         match self {
209    //             EntityMessage::EnableDelegationResponse(sub_id, entity) => {
210    //                 EntityMessage::EnableDelegationResponse(sub_id, entity.to_host())
211    //             }
212    //             EntityMessage::MigrateResponse(sub_id, entity, other_entity) => {
213    //                 EntityMessage::MigrateResponse(sub_id, entity.to_host(), other_entity)
214    //             }
215    //             EntityMessage::RequestAuthority(sub_id, entity) => {
216    //                 EntityMessage::RequestAuthority(sub_id, entity.to_host())
217    //             }
218    //             EntityMessage::ReleaseAuthority(_, _) => panic!("EntityReleaseAuthority should not call `to_host_message()`"),
219    //             msg => {
220    //                 panic!("No reason to convert message {:?} to HostEntity", msg);
221    //             }
222    //         }
223    //     }
224    //
225    /// Converts this remote-entity message into an `EntityEvent`, resolving the entity via `local_entity_map`.
226    pub fn to_event(self, local_entity_map: &LocalEntityMap) -> EntityEvent {
227        let remote_entity = self.entity().unwrap();
228        let global_entity = match local_entity_map.global_entity_from_remote(&remote_entity) {
229            Some(ge) => *ge,
230            None => {
231                error!("to_event() failed to find RemoteEntity({:?}) in entity_map! Message type: {:?}", 
232                    remote_entity, self.get_type());
233                panic!("RemoteEntity not found in entity_map during to_event conversion");
234            }
235        };
236        match self {
237            EntityMessage::Publish(_, _) => EntityEvent::Publish(global_entity),
238            EntityMessage::Unpublish(_, _) => EntityEvent::Unpublish(global_entity),
239            EntityMessage::EnableDelegation(_, _) => EntityEvent::EnableDelegation(global_entity),
240            EntityMessage::EnableDelegationResponse(_, _) => {
241                panic!("EnableDelegationResponse should not be sent by remote")
242            }
243            EntityMessage::DisableDelegation(_, _) => EntityEvent::DisableDelegation(global_entity),
244            EntityMessage::RequestAuthority(_, _) => EntityEvent::RequestAuthority(global_entity),
245            EntityMessage::ReleaseAuthority(_, _) => EntityEvent::ReleaseAuthority(global_entity),
246            EntityMessage::SetAuthority(_, _, status) => {
247                EntityEvent::SetAuthority(global_entity, status)
248            }
249            EntityMessage::MigrateResponse(_, _, _new_remote_entity) => {
250                // MigrateResponse should never be EntityMessage<RemoteEntity>!
251                // It should be EntityMessage<HostEntity> so the client can look it up
252                panic!("MigrateResponse should be EntityMessage<HostEntity>, not EntityMessage<RemoteEntity>!");
253            }
254            EntityMessage::Spawn(_)
255            | EntityMessage::SpawnWithComponents(_, _)
256            | EntityMessage::Despawn(_)
257            | EntityMessage::InsertComponent(_, _)
258            | EntityMessage::RemoveComponent(_, _) => panic!("Handled elsewhere"),
259            EntityMessage::Noop => panic!("Cannot convert Noop message to an event"),
260        }
261    }
262}
263//
264impl EntityMessage<HostEntity> {
265    /// Converts this host-entity message into an `EntityEvent`, or `None` if the entity is not found in the map.
266    pub fn to_event(self, local_entity_map: &LocalEntityMap) -> Option<EntityEvent> {
267        let host_entity = self.entity().unwrap();
268        let global_entity = match local_entity_map.global_entity_from_host(&host_entity) {
269            Some(ge) => *ge,
270            None => {
271                error!(
272                    "to_event() failed to find HostEntity({:?}) in entity_map — message type: {:?}; skipping",
273                    host_entity,
274                    self.get_type()
275                );
276                return None;
277            }
278        };
279        Some(match self {
280            EntityMessage::Publish(_, _) => EntityEvent::Publish(global_entity),
281            EntityMessage::Unpublish(_, _) => EntityEvent::Unpublish(global_entity),
282            EntityMessage::EnableDelegation(_, _) => EntityEvent::EnableDelegation(global_entity),
283            EntityMessage::EnableDelegationResponse(_, _) => {
284                EntityEvent::EnableDelegationResponse(global_entity)
285            }
286            EntityMessage::DisableDelegation(_, _) => EntityEvent::DisableDelegation(global_entity),
287            EntityMessage::RequestAuthority(_, _) => EntityEvent::RequestAuthority(global_entity),
288            EntityMessage::ReleaseAuthority(_, _) => EntityEvent::ReleaseAuthority(global_entity),
289            EntityMessage::SetAuthority(_, _, status) => {
290                EntityEvent::SetAuthority(global_entity, status)
291            }
292            EntityMessage::MigrateResponse(_, _, new_remote_entity) => {
293                EntityEvent::MigrateResponse(global_entity, new_remote_entity)
294            }
295            EntityMessage::Spawn(_)
296            | EntityMessage::SpawnWithComponents(_, _)
297            | EntityMessage::Despawn(_)
298            | EntityMessage::InsertComponent(_, _)
299            | EntityMessage::RemoveComponent(_, _) => panic!("Handled elsewhere"),
300            EntityMessage::Noop => panic!("Cannot convert Noop message to an event"),
301        })
302    }
303}