veloren_common_net/sync/
packet.rs

1use super::track::UpdateTracker;
2use common::{resources::Time, uid::Uid};
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use specs::{Component, Entity, Join, ReadStorage, World, WorldExt, storage::AccessMut};
5use std::{
6    convert::{TryFrom, TryInto},
7    fmt::Debug,
8    marker::PhantomData,
9    num::NonZeroU64,
10};
11use tracing::error;
12
13// TODO: apply_{insert,modify,remove} all take the entity and call
14// `write_storage` once per entity per component, instead of once per update
15// batch(e.g. in a system-like memory access pattern); if sync ends up being a
16// bottleneck, try optimizing this
17/// Implemented by type that carries component data for insertion and
18/// modification The assocatied `Phantom` type only carries information about
19/// which component type is of interest and is used to transmit deletion events
20pub trait CompPacket: Clone + Debug + Send + 'static {
21    type Phantom: Clone + Debug + Serialize + DeserializeOwned;
22
23    fn apply_insert(self, entity: Entity, world: &World, force_update: bool);
24    fn apply_modify(self, entity: Entity, world: &World, force_update: bool);
25    fn apply_remove(phantom: Self::Phantom, entity: Entity, world: &World);
26}
27
28/// Useful for implementing CompPacket trait
29pub fn handle_insert<C: Component>(comp: C, entity: Entity, world: &World) {
30    if let Err(e) = world.write_storage::<C>().insert(entity, comp) {
31        error!(?e, "Error inserting");
32    }
33}
34/// Useful for implementing CompPacket trait
35pub fn handle_modify<C: Component + Debug>(comp: C, entity: Entity, world: &World) {
36    if let Some(mut c) = world.write_storage::<C>().get_mut(entity) {
37        *c.access_mut() = comp
38    } else {
39        error!(
40            ?comp,
41            "Error modifying synced component, it doesn't seem to exist"
42        );
43    }
44}
45/// Useful for implementing CompPacket trait
46pub fn handle_remove<C: Component>(entity: Entity, world: &World) {
47    world.write_storage::<C>().remove(entity);
48}
49
50pub trait InterpolatableComponent: Component {
51    type InterpData: Component;
52    type ReadData;
53
54    fn new_data(x: Self) -> Self::InterpData;
55    fn update_component(&self, data: &mut Self::InterpData, time: f64, force_update: bool);
56    #[must_use]
57    fn interpolate(self, data: &Self::InterpData, time: f64, read_data: &Self::ReadData) -> Self;
58}
59
60pub fn handle_interp_insert<C: InterpolatableComponent + Clone>(
61    comp: C,
62    entity: Entity,
63    world: &World,
64    force_update: bool,
65) {
66    let mut interp_data = C::new_data(comp.clone());
67    let time = world.read_resource::<Time>().0;
68    comp.update_component(&mut interp_data, time, force_update);
69    handle_insert(comp, entity, world);
70    handle_insert(interp_data, entity, world);
71}
72
73pub fn handle_interp_modify<C: InterpolatableComponent + Debug>(
74    comp: C,
75    entity: Entity,
76    world: &World,
77    force_update: bool,
78) {
79    if let Some(mut interp_data) = world.write_storage::<C::InterpData>().get_mut(entity) {
80        let time = world.read_resource::<Time>().0;
81        comp.update_component(interp_data.access_mut(), time, force_update);
82        handle_modify(comp, entity, world);
83    } else {
84        error!(
85            ?comp,
86            "Error modifying interpolation data for synced component, it doesn't seem to exist"
87        );
88    }
89}
90
91pub fn handle_interp_remove<C: InterpolatableComponent>(entity: Entity, world: &World) {
92    handle_remove::<C>(entity, world);
93    handle_remove::<C::InterpData>(entity, world);
94}
95
96#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
97pub enum CompUpdateKind<P: CompPacket> {
98    Inserted(P),
99    Modified(P),
100    Removed(P::Phantom),
101}
102
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct EntityPackage<P: CompPacket> {
105    pub uid: Uid,
106    pub comps: Vec<P>,
107}
108
109#[derive(Clone, Debug, Serialize, Deserialize)]
110pub struct EntitySyncPackage {
111    pub created_entities: Vec<Uid>,
112    pub deleted_entities: Vec<Uid>,
113}
114impl EntitySyncPackage {
115    pub fn new(
116        uids: &ReadStorage<'_, Uid>,
117        uid_tracker: &UpdateTracker<Uid>,
118        filter: impl Join + Copy,
119        deleted_entities: Vec<Uid>,
120    ) -> Self {
121        // Add created and deleted entities
122        let created_entities = (uids, filter, uid_tracker.inserted())
123            .join()
124            .map(|(uid, _, _)| *uid)
125            .collect();
126
127        Self {
128            created_entities,
129            deleted_entities,
130        }
131    }
132}
133
134#[derive(Clone, Debug, Serialize, Deserialize)]
135pub struct CompSyncPackage<P: CompPacket> {
136    // TODO: this can be made to take less space by clumping updates for the same entity together
137    pub comp_updates: Vec<(NonZeroU64, CompUpdateKind<P>)>,
138}
139
140impl<P: CompPacket> CompSyncPackage<P> {
141    #[expect(clippy::new_without_default)]
142    pub fn new() -> Self {
143        Self {
144            comp_updates: Vec::new(),
145        }
146    }
147
148    pub fn comp_inserted<C>(&mut self, uid: Uid, comp: C)
149    where
150        P: From<C>,
151    {
152        self.comp_updates
153            .push((uid.into(), CompUpdateKind::Inserted(comp.into())));
154    }
155
156    pub fn comp_modified<C>(&mut self, uid: Uid, comp: C)
157    where
158        P: From<C>,
159    {
160        self.comp_updates
161            .push((uid.into(), CompUpdateKind::Modified(comp.into())));
162    }
163
164    pub fn comp_removed<C>(&mut self, uid: Uid)
165    where
166        P::Phantom: From<PhantomData<C>>,
167    {
168        self.comp_updates
169            .push((uid.0, CompUpdateKind::Removed(PhantomData::<C>.into())));
170    }
171
172    pub fn add_component_updates<'a, C>(
173        &mut self,
174        uids: &ReadStorage<'a, Uid>,
175        tracker: &UpdateTracker<C>,
176        storage: &ReadStorage<'a, C>,
177        filter: impl Join + Copy,
178    ) where
179        P: From<C>,
180        C: Component + Clone + Send + Sync + TryFrom<P>,
181        P::Phantom: From<PhantomData<C>>,
182        P::Phantom: TryInto<PhantomData<C>>,
183        C::Storage: specs::storage::Tracked,
184    {
185        tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
186    }
187
188    /// If there was an update to the component `C` on the provided entity this
189    /// will add the update to this package.
190    pub fn add_component_update<C>(
191        &mut self,
192        tracker: &UpdateTracker<C>,
193        storage: &ReadStorage<'_, C>,
194        uid: NonZeroU64,
195        entity: Entity,
196        force_sync: bool,
197    ) where
198        P: From<C>,
199        C: Component + Clone + Send + Sync + TryFrom<P>,
200        P::Phantom: From<PhantomData<C>>,
201        P::Phantom: TryInto<PhantomData<C>>,
202        C::Storage: specs::storage::Tracked,
203    {
204        if let Some(comp_update) = tracker.get_update(storage, entity, force_sync) {
205            self.comp_updates.push((uid, comp_update))
206        }
207    }
208
209    /// Returns whether this package is empty, useful for not sending an empty
210    /// message.
211    pub fn is_empty(&self) -> bool { self.comp_updates.is_empty() }
212}