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