veloren_common_net/sync/
track.rs

1use super::packet::{CompPacket, CompUpdateKind};
2use common::uid::Uid;
3use specs::{BitSet, Component, Entity, Join, ReadStorage, World, WorldExt};
4use std::{
5    convert::{TryFrom, TryInto},
6    marker::PhantomData,
7    num::NonZeroU64,
8};
9
10pub struct UpdateTracker<C: Component> {
11    reader_id: specs::ReaderId<specs::storage::ComponentEvent>,
12    inserted: BitSet,
13    modified: BitSet,
14    removed: BitSet,
15    phantom: PhantomData<C>,
16}
17impl<C: Component> UpdateTracker<C>
18where
19    C::Storage: specs::storage::Tracked,
20{
21    pub fn new(specs_world: &World) -> Self {
22        Self {
23            reader_id: specs_world.write_storage::<C>().register_reader(),
24            inserted: BitSet::new(),
25            modified: BitSet::new(),
26            removed: BitSet::new(),
27            phantom: PhantomData,
28        }
29    }
30
31    pub fn inserted(&self) -> &BitSet { &self.inserted }
32
33    pub fn modified(&self) -> &BitSet { &self.modified }
34
35    pub fn removed(&self) -> &BitSet { &self.removed }
36
37    pub fn record_changes(&mut self, storage: &ReadStorage<'_, C>) {
38        self.inserted.clear();
39        self.modified.clear();
40        self.removed.clear();
41
42        for event in storage.channel().read(&mut self.reader_id) {
43            match event {
44                specs::storage::ComponentEvent::Inserted(id) => {
45                    // If previously removed/modified we don't need to know that anymore
46                    self.removed.remove(*id);
47                    self.modified.remove(*id);
48                    self.inserted.add(*id);
49                },
50                specs::storage::ComponentEvent::Modified(id) => {
51                    // We don't care about modification if the component was just added
52                    if !self.inserted.contains(*id) {
53                        debug_assert!(!self.removed.contains(*id)); // Theoretically impossible
54                        self.modified.add(*id);
55                    }
56                },
57                specs::storage::ComponentEvent::Removed(id) => {
58                    // Don't need to know that it was inserted/modified if it was subsequently
59                    // removed
60                    self.inserted.remove(*id);
61                    self.modified.remove(*id);
62                    self.removed.add(*id);
63                },
64            };
65        }
66    }
67}
68
69impl<C: Component + Clone + Send + Sync> UpdateTracker<C> {
70    pub fn add_packet_for<P>(
71        &self,
72        storage: &ReadStorage<'_, C>,
73        entity: Entity,
74        packets: &mut Vec<P>,
75    ) where
76        P: CompPacket,
77        P: From<C>,
78        C: TryFrom<P>,
79        P::Phantom: From<PhantomData<C>>,
80        P::Phantom: TryInto<PhantomData<C>>,
81        C::Storage: specs::storage::Tracked,
82    {
83        if let Some(comp) = storage.get(entity) {
84            packets.push(P::from(comp.clone()));
85        }
86    }
87
88    pub fn get_updates_for<P>(
89        &self,
90        uids: &ReadStorage<'_, Uid>,
91        storage: &ReadStorage<'_, C>,
92        entity_filter: impl Join + Copy,
93        buf: &mut Vec<(NonZeroU64, CompUpdateKind<P>)>,
94    ) where
95        P: CompPacket,
96        P: From<C>,
97        C: TryFrom<P>,
98        P::Phantom: From<PhantomData<C>>,
99        P::Phantom: TryInto<PhantomData<C>>,
100        C::Storage: specs::storage::Tracked,
101    {
102        // Generate inserted updates
103        for (uid, comp, _, _) in (uids, storage, &self.inserted, entity_filter).join() {
104            buf.push((
105                (*uid).into(),
106                CompUpdateKind::Inserted(P::from(comp.clone())),
107            ));
108        }
109
110        // Generate modified updates
111        for (uid, comp, _, _) in (uids, storage, &self.modified, entity_filter).join() {
112            buf.push((
113                (*uid).into(),
114                CompUpdateKind::Modified(P::from(comp.clone())),
115            ));
116        }
117
118        // Generate removed updates
119        for (uid, _, _) in (uids, &self.removed, entity_filter).join() {
120            buf.push((
121                (*uid).into(),
122                CompUpdateKind::Removed(P::Phantom::from(PhantomData::<C>)),
123            ));
124        }
125    }
126
127    /// Returns `Some(update)` if the tracked component was modified for this
128    /// entity.
129    pub fn get_update<P>(
130        &self,
131        storage: &ReadStorage<'_, C>,
132        entity: Entity,
133        force_sync: bool,
134    ) -> Option<CompUpdateKind<P>>
135    where
136        P: CompPacket,
137        P: From<C>,
138        C: TryFrom<P>,
139        P::Phantom: From<PhantomData<C>>,
140        P::Phantom: TryInto<PhantomData<C>>,
141        C::Storage: specs::storage::Tracked,
142    {
143        let id = entity.id();
144        // Generate update if one exists.
145        //
146        // Note: presence of the id in these bitsets should be mutually exclusive
147        if force_sync {
148            storage
149                .get(entity)
150                .map(|comp| CompUpdateKind::Inserted(P::from(comp.clone())))
151        } else if self.modified.contains(id) {
152            storage
153                .get(entity)
154                .map(|comp| CompUpdateKind::Modified(P::from(comp.clone())))
155        } else if self.inserted.contains(id) {
156            storage
157                .get(entity)
158                .map(|comp| CompUpdateKind::Inserted(P::from(comp.clone())))
159        } else if self.removed.contains(id) {
160            Some(CompUpdateKind::Removed(P::Phantom::from(PhantomData::<C>)))
161        } else {
162            None
163        }
164    }
165}