Skip to main content

sdec_bevy/
schema.rs

1use std::any::TypeId;
2use std::collections::HashMap;
3use std::marker::PhantomData;
4
5use anyhow::{anyhow, Result};
6use bevy_ecs::component::Mutable;
7use bevy_ecs::prelude::{Component, Entity, World};
8use codec::{ComponentSnapshot, DeltaUpdateComponent, DeltaUpdateEntity, FieldValue};
9use schema::{ChangePolicy, ComponentDef, ComponentId, FieldCodec, FieldDef, FieldId, Schema};
10
11#[derive(Debug, Clone)]
12pub struct ReplicatedField {
13    pub id: u16,
14    pub codec: FieldCodec,
15    pub change: Option<ChangePolicy>,
16}
17
18pub trait ReplicatedComponent: Component<Mutability = Mutable> {
19    const COMPONENT_ID: u16;
20
21    fn fields() -> Vec<ReplicatedField>;
22
23    fn read_fields(&self) -> Vec<FieldValue>;
24
25    fn apply_field(&mut self, index: usize, value: FieldValue) -> Result<()>;
26
27    fn from_fields(fields: &[FieldValue]) -> Result<Self>
28    where
29        Self: Sized;
30}
31
32pub(crate) trait ComponentAdapter {
33    fn type_id(&self) -> TypeId;
34    fn component_id(&self) -> ComponentId;
35    fn schema_def(&self) -> ComponentDef;
36    fn snapshot_component(&self, world: &World, entity: Entity) -> Option<ComponentSnapshot>;
37    fn update_component(&self, world: &World, entity: Entity) -> Option<DeltaUpdateComponent>;
38    fn apply_update(
39        &self,
40        world: &mut World,
41        entity: Entity,
42        fields: &[(usize, FieldValue)],
43    ) -> Result<()>;
44    fn insert_component(
45        &self,
46        world: &mut World,
47        entity: Entity,
48        fields: &[FieldValue],
49    ) -> Result<()>;
50    fn added_entities(&self, world: &mut World) -> Vec<Entity>;
51    fn changed_entities(&self, world: &mut World) -> Vec<Entity>;
52    fn removed_entities(&self, world: &World) -> Vec<Entity>;
53}
54
55struct ComponentAdapterImpl<T: ReplicatedComponent> {
56    component_id: ComponentId,
57    fields: Vec<ReplicatedField>,
58    _marker: PhantomData<T>,
59}
60
61impl<T: ReplicatedComponent> ComponentAdapterImpl<T> {
62    fn new() -> Self {
63        Self {
64            component_id: ComponentId::new(T::COMPONENT_ID).expect("component id must be non-zero"),
65            fields: T::fields(),
66            _marker: PhantomData,
67        }
68    }
69
70    fn snapshot_fields(&self, component: &T) -> Vec<FieldValue> {
71        component.read_fields()
72    }
73
74    fn build_field_defs(&self) -> Vec<FieldDef> {
75        self.fields
76            .iter()
77            .map(|field| {
78                let mut def = FieldDef::new(
79                    FieldId::new(field.id).expect("field id must be non-zero"),
80                    field.codec,
81                );
82                if let Some(change) = field.change {
83                    def = def.change(change);
84                }
85                def
86            })
87            .collect()
88    }
89}
90
91impl<T: ReplicatedComponent> ComponentAdapter for ComponentAdapterImpl<T> {
92    fn type_id(&self) -> TypeId {
93        TypeId::of::<T>()
94    }
95
96    fn component_id(&self) -> ComponentId {
97        self.component_id
98    }
99
100    fn schema_def(&self) -> ComponentDef {
101        let mut def = ComponentDef::new(self.component_id);
102        for field in self.build_field_defs() {
103            def = def.field(field);
104        }
105        def
106    }
107
108    fn snapshot_component(&self, world: &World, entity: Entity) -> Option<ComponentSnapshot> {
109        let component = world.get::<T>(entity)?;
110        let fields = self.snapshot_fields(component);
111        Some(ComponentSnapshot {
112            id: self.component_id,
113            fields,
114        })
115    }
116
117    fn update_component(&self, world: &World, entity: Entity) -> Option<DeltaUpdateComponent> {
118        let component = world.get::<T>(entity)?;
119        let fields = self.snapshot_fields(component);
120        let updates = fields.into_iter().enumerate().collect();
121        Some(DeltaUpdateComponent {
122            id: self.component_id,
123            fields: updates,
124        })
125    }
126
127    fn apply_update(
128        &self,
129        world: &mut World,
130        entity: Entity,
131        fields: &[(usize, FieldValue)],
132    ) -> Result<()> {
133        let mut component = world
134            .get_mut::<T>(entity)
135            .ok_or_else(|| anyhow!("missing component {:?}", self.component_id))?;
136        for (index, value) in fields {
137            component.apply_field(*index, *value)?;
138        }
139        Ok(())
140    }
141
142    fn insert_component(
143        &self,
144        world: &mut World,
145        entity: Entity,
146        fields: &[FieldValue],
147    ) -> Result<()> {
148        let component = T::from_fields(fields)?;
149        world.entity_mut(entity).insert(component);
150        Ok(())
151    }
152
153    fn added_entities(&self, world: &mut World) -> Vec<Entity> {
154        let mut query = world.query_filtered::<Entity, bevy_ecs::query::Added<T>>();
155        query.iter(world).collect()
156    }
157
158    fn changed_entities(&self, world: &mut World) -> Vec<Entity> {
159        let mut query = world.query_filtered::<Entity, bevy_ecs::query::Changed<T>>();
160        query.iter(world).collect()
161    }
162
163    fn removed_entities(&self, world: &World) -> Vec<Entity> {
164        world.removed::<T>().collect()
165    }
166}
167
168pub struct BevySchema {
169    pub schema: Schema,
170    adapters: Vec<Box<dyn ComponentAdapter>>,
171    adapter_by_component: HashMap<ComponentId, usize>,
172}
173
174impl BevySchema {
175    #[must_use]
176    pub fn schema(&self) -> &Schema {
177        &self.schema
178    }
179
180    pub(crate) fn adapters(&self) -> &[Box<dyn ComponentAdapter>] {
181        &self.adapters
182    }
183
184    pub(crate) fn adapter_by_component(
185        &self,
186        component_id: ComponentId,
187    ) -> Option<&dyn ComponentAdapter> {
188        let index = self.adapter_by_component.get(&component_id).copied()?;
189        self.adapters.get(index).map(|adapter| adapter.as_ref())
190    }
191
192    pub fn snapshot_entity(&self, world: &World, entity: Entity) -> Vec<ComponentSnapshot> {
193        self.adapters
194            .iter()
195            .filter_map(|adapter| adapter.snapshot_component(world, entity))
196            .collect()
197    }
198
199    pub fn apply_component_fields(
200        &self,
201        world: &mut World,
202        entity: Entity,
203        component_id: ComponentId,
204        fields: &[(usize, FieldValue)],
205    ) -> Result<()> {
206        let adapter = self
207            .adapter_by_component(component_id)
208            .ok_or_else(|| anyhow!("unknown component {:?}", component_id))?;
209        adapter.apply_update(world, entity, fields)
210    }
211
212    pub fn insert_component_fields(
213        &self,
214        world: &mut World,
215        entity: Entity,
216        component_id: ComponentId,
217        fields: &[FieldValue],
218    ) -> Result<()> {
219        let adapter = self
220            .adapter_by_component(component_id)
221            .ok_or_else(|| anyhow!("unknown component {:?}", component_id))?;
222        adapter.insert_component(world, entity, fields)
223    }
224
225    pub fn build_delta_update(
226        &self,
227        world: &World,
228        entity: Entity,
229        entity_id: codec::EntityId,
230        component_ids: &[ComponentId],
231    ) -> Option<DeltaUpdateEntity> {
232        let mut components = Vec::with_capacity(component_ids.len());
233        for component_id in component_ids {
234            let adapter = self.adapter_by_component(*component_id)?;
235            if let Some(update) = adapter.update_component(world, entity) {
236                components.push(update);
237            }
238        }
239        if components.is_empty() {
240            None
241        } else {
242            Some(DeltaUpdateEntity {
243                id: entity_id,
244                components,
245            })
246        }
247    }
248}
249
250#[derive(Default)]
251pub struct BevySchemaBuilder {
252    adapters: Vec<Box<dyn ComponentAdapter>>,
253}
254
255impl BevySchemaBuilder {
256    #[must_use]
257    pub fn new() -> Self {
258        Self::default()
259    }
260
261    pub fn component<T: ReplicatedComponent + 'static>(&mut self) -> &mut Self {
262        let adapter = ComponentAdapterImpl::<T>::new();
263        if self
264            .adapters
265            .iter()
266            .any(|existing| existing.type_id() == adapter.type_id())
267        {
268            return self;
269        }
270        self.adapters.push(Box::new(adapter));
271        self
272    }
273
274    pub fn build(self) -> Result<BevySchema> {
275        let mut components = Vec::with_capacity(self.adapters.len());
276        for adapter in &self.adapters {
277            components.push(adapter.schema_def());
278        }
279        let schema = Schema::new(components).map_err(|err| anyhow!("{err:?}"))?;
280        let adapter_by_component = self
281            .adapters
282            .iter()
283            .enumerate()
284            .map(|(index, adapter)| (adapter.component_id(), index))
285            .collect();
286        Ok(BevySchema {
287            schema,
288            adapters: self.adapters,
289            adapter_by_component,
290        })
291    }
292}