veloren_common_net/sync/
sync_ext.rs

1use super::{
2    packet::{CompPacket, CompSyncPackage, CompUpdateKind, EntityPackage, EntitySyncPackage},
3    track::UpdateTracker,
4};
5use common::{
6    resources::PlayerEntity,
7    uid::{IdMaps, Uid},
8};
9use specs::{WorldExt, world::Builder};
10use tracing::error;
11
12pub trait WorldSyncExt {
13    fn register_sync_marker(&mut self);
14    fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
15    where
16        C::Storage: Default + specs::storage::Tracked;
17    fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
18    where
19        C::Storage: Default + specs::storage::Tracked;
20    fn create_entity_synced(&mut self) -> specs::EntityBuilder;
21    fn delete_entity_and_clear_uid_mapping(&mut self, uid: Uid);
22    fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid>;
23    fn entity_from_uid(&self, uid: Uid) -> Option<specs::Entity>;
24    fn apply_entity_package<P: CompPacket>(
25        &mut self,
26        entity_package: EntityPackage<P>,
27    ) -> specs::Entity;
28    fn apply_entity_sync_package(&mut self, package: EntitySyncPackage, client_uid: Option<Uid>);
29    fn apply_comp_sync_package<P: CompPacket>(&mut self, package: CompSyncPackage<P>);
30}
31
32impl WorldSyncExt for specs::World {
33    fn register_sync_marker(&mut self) {
34        self.register_synced::<Uid>();
35        self.insert(IdMaps::new());
36    }
37
38    fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
39    where
40        C::Storage: Default + specs::storage::Tracked,
41    {
42        self.register::<C>();
43        self.register_tracker::<C>();
44    }
45
46    fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
47    where
48        C::Storage: Default + specs::storage::Tracked,
49    {
50        let tracker = UpdateTracker::<C>::new(self);
51        self.insert(tracker);
52    }
53
54    fn create_entity_synced(&mut self) -> specs::EntityBuilder {
55        // TODO: Add metric for number of new entities created in a tick? Most
56        // convenient would be to store counter in `IdMaps` so that we don't
57        // have to fetch another resource nor require an additional parameter here
58        // nor use globals.
59        let builder = self.create_entity();
60        let uid = builder
61            .world
62            .write_resource::<IdMaps>()
63            .allocate(builder.entity);
64        builder.with(uid)
65    }
66
67    /// This method should be used from the client-side when processing network
68    /// messages that delete entities.
69    ///
70    /// Only used on the client.
71    fn delete_entity_and_clear_uid_mapping(&mut self, uid: Uid) {
72        // Clear from uid allocator
73        let maybe_entity = self.write_resource::<IdMaps>()
74                // Note, rtsim entity and character id mappings don't exist on the client but it is
75                // easier to reuse the same structure here since there is shared code that needs to
76                // lookup the entity from a uid.
77                .remove_entity(None, Some(uid), None, None);
78        if let Some(entity) = maybe_entity {
79            if let Err(e) = self.delete_entity(entity) {
80                error!(?e, "Failed to delete entity");
81            }
82        }
83    }
84
85    /// Get the UID of an entity
86    fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid> {
87        self.read_storage::<Uid>().get(entity).copied()
88    }
89
90    /// Get an entity from a UID
91    fn entity_from_uid(&self, uid: Uid) -> Option<specs::Entity> {
92        self.read_resource::<IdMaps>().uid_entity(uid)
93    }
94
95    fn apply_entity_package<P: CompPacket>(
96        &mut self,
97        entity_package: EntityPackage<P>,
98    ) -> specs::Entity {
99        let EntityPackage { uid, comps } = entity_package;
100
101        let entity = create_entity_with_uid(self, uid);
102        for packet in comps {
103            packet.apply_insert(entity, self, true)
104        }
105
106        entity
107    }
108
109    fn apply_entity_sync_package(&mut self, package: EntitySyncPackage, client_uid: Option<Uid>) {
110        // Take ownership of the fields
111        let EntitySyncPackage {
112            created_entities,
113            deleted_entities,
114        } = package;
115
116        // Attempt to create entities
117        created_entities.into_iter().for_each(|uid| {
118            create_entity_with_uid(self, uid);
119        });
120
121        // Attempt to delete entities that were marked for deletion
122        deleted_entities.into_iter().for_each(|uid| {
123            // Skip deleting the client's own entity. It will appear here when exiting
124            // "in-game" and the client already handles cleaning things up
125            // there. Although the client might not get this anyway since it
126            // should no longer be subscribed to any regions.
127            if client_uid != Some(uid) {
128                self.delete_entity_and_clear_uid_mapping(uid);
129            }
130        });
131    }
132
133    fn apply_comp_sync_package<P: CompPacket>(&mut self, package: CompSyncPackage<P>) {
134        // Update components
135        let player_entity = self.read_resource::<PlayerEntity>().0;
136        package.comp_updates.into_iter().for_each(|(uid, update)| {
137            if let Some(entity) = self.read_resource::<IdMaps>().uid_entity(uid.into()) {
138                let force_update = player_entity == Some(entity);
139                match update {
140                    CompUpdateKind::Inserted(packet) => {
141                        packet.apply_insert(entity, self, force_update);
142                    },
143                    CompUpdateKind::Modified(packet) => {
144                        packet.apply_modify(entity, self, force_update);
145                    },
146                    CompUpdateKind::Removed(phantom) => {
147                        P::apply_remove(phantom, entity, self);
148                    },
149                }
150            }
151        });
152    }
153}
154
155// Private utilities
156//
157// Only used on the client.
158fn create_entity_with_uid(specs_world: &mut specs::World, entity_uid: Uid) -> specs::Entity {
159    let existing_entity = specs_world.read_resource::<IdMaps>().uid_entity(entity_uid);
160
161    // TODO: Are there any expected cases where there is an existing entity with
162    // this UID? If not, we may want to log an error. Otherwise, it may be useful to
163    // document these cases.
164    match existing_entity {
165        Some(entity) => entity,
166        None => {
167            let entity_builder = specs_world.create_entity();
168            entity_builder
169                .world
170                .write_resource::<IdMaps>()
171                .add_entity(entity_uid, entity_builder.entity);
172            entity_builder.with(entity_uid).build()
173        },
174    }
175}