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