-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathreplication_rules.rs
More file actions
566 lines (474 loc) · 17.7 KB
/
replication_rules.rs
File metadata and controls
566 lines (474 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
use core::cmp::Reverse;
use bevy::{
ecs::{archetype::Archetype, component::ComponentId, entity::MapEntities},
platform::collections::HashSet,
prelude::*,
};
use serde::{Serialize, de::DeserializeOwned};
use super::replication_registry::{
FnsId, ReplicationRegistry, command_fns::MutWrite, rule_fns::RuleFns,
};
/// Replication functions for [`App`].
pub trait AppRuleExt {
/// Creates a replication rule for a single component.
///
/// The component will be replicated if its entity contains the [`Replicated`](super::Replicated)
/// marker component.
///
/// Component will be serialized and deserialized as-is using postcard.
/// To customize it, use [`Self::replicate_group`].
///
/// See also [`Self::replicate_with`] and the section on [`components`](../../index.html#components)
/// from the quick start guide.
fn replicate<C>(&mut self) -> &mut Self
where
C: Component<Mutability: MutWrite<C>> + Serialize + DeserializeOwned,
{
self.replicate_with::<C>(RuleFns::default())
}
#[deprecated(note = "no longer needed, just use `replicate` instead")]
fn replicate_mapped<C>(&mut self) -> &mut Self
where
C: Component<Mutability: MutWrite<C>> + Serialize + DeserializeOwned + MapEntities,
{
self.replicate::<C>()
}
/**
Same as [`Self::replicate`], but uses the specified functions for serialization and deserialization.
Can be used to customize how the component will be passed over the network or
for components that don't implement [`Serialize`] or [`DeserializeOwned`].
You can also override how the component will be written,
see [`AppMarkerExt`](super::command_markers::AppMarkerExt).
<div class="warning">
If your component contains an [`Entity`] inside, don't forget to call [`Component::map_entities`]
in your deserialization function.
</div>
See also [`postcard_utils`](crate::shared::postcard_utils).
# Examples
Ser/de only specific field:
```
use bevy::prelude::*;
use bevy_replicon::{
bytes::Bytes,
shared::{
postcard_utils,
replication::replication_registry::{
ctx::{SerializeCtx, WriteCtx},
rule_fns::{RuleFns, DeserializeFn},
},
},
prelude::*,
};
# let mut app = App::new();
# app.add_plugins(RepliconPlugins);
// We override in-place as well to apply only translation when the component is already inserted.
app.replicate_with(
RuleFns::new(serialize_translation, deserialize_translation)
.with_in_place(deserialize_transform_in_place),
);
/// Serializes only `translation` from [`Transform`].
fn serialize_translation(
_ctx: &SerializeCtx,
transform: &Transform,
message: &mut Vec<u8>,
) -> Result<()> {
postcard_utils::to_extend_mut(&transform.translation, message)?;
Ok(())
}
/// Deserializes `translation` and creates [`Transform`] from it.
///
/// Called by Replicon on component insertions.
fn deserialize_translation(
_ctx: &mut WriteCtx,
message: &mut Bytes,
) -> Result<Transform> {
let translation: Vec3 = postcard_utils::from_buf(message)?;
Ok(Transform::from_translation(translation))
}
/// Applies the assigned deserialization function and assigns only translation.
///
/// Called by Replicon on component mutations.
fn deserialize_transform_in_place(
deserialize: DeserializeFn<Transform>,
ctx: &mut WriteCtx,
component: &mut Transform,
message: &mut Bytes,
) -> Result<()> {
let transform = (deserialize)(ctx, message)?;
component.translation = transform.translation;
Ok(())
}
```
Ser/de with compression:
```
use bevy::prelude::*;
use bevy_replicon::{
bytes::Bytes,
shared::{
postcard_utils,
replication::replication_registry::{
ctx::{SerializeCtx, WriteCtx},
rule_fns::RuleFns,
},
},
postcard,
prelude::*,
};
use bytes::Buf;
use serde::{Deserialize, Serialize};
# let mut app = App::new();
# app.add_plugins(RepliconPlugins);
app.replicate_with(RuleFns::new(
serialize_big_component,
deserialize_big_component,
));
fn serialize_big_component(
_ctx: &SerializeCtx,
component: &BigComponent,
message: &mut Vec<u8>,
) -> Result<()> {
// Serialize as usual, but track size.
let start = message.len();
postcard_utils::to_extend_mut(component, message)?;
let end = message.len();
// Compress serialized slice.
// Could be `zstd`, for example.
let compressed = compress(&mut message[start..end]);
// Replace serialized slice with compressed data prepended by its size.
message.truncate(start);
postcard_utils::to_extend_mut(&compressed.len(), message)?;
message.extend(compressed);
Ok(())
}
fn deserialize_big_component(
_ctx: &mut WriteCtx,
message: &mut Bytes,
) -> Result<BigComponent> {
// Read size first to know how much data is encoded.
let size = postcard_utils::from_buf(message)?;
// Apply decompression and advance the reading cursor.
let decompressed = decompress(&message[..size]);
message.advance(size);
let component = postcard::from_bytes(&decompressed)?;
Ok(component)
}
#[derive(Component, Deserialize, Serialize)]
struct BigComponent(Vec<u64>);
# fn compress(data: &[u8]) -> Vec<u8> { unimplemented!() }
# fn decompress(data: &[u8]) -> Vec<u8> { unimplemented!() }
```
Custom ser/de with entity mapping:
```
use bevy::prelude::*;
use bevy_replicon::{
bytes::Bytes,
shared::{
postcard_utils,
replication::replication_registry::{
ctx::{SerializeCtx, WriteCtx},
rule_fns::RuleFns,
},
},
postcard,
prelude::*,
};
use serde::{Deserialize, Serialize};
let mut app = App::new();
app.add_plugins(RepliconPlugins);
app.replicate_with(RuleFns::new(
serialize_mapped_component,
deserialize_mapped_component,
));
/// Serializes [`MappedComponent`], but skips [`MappedComponent::unused_field`].
fn serialize_mapped_component(
_ctx: &SerializeCtx,
component: &MappedComponent,
message: &mut Vec<u8>,
) -> Result<()> {
postcard_utils::to_extend_mut(&component.entity, message)?;
Ok(())
}
/// Deserializes an entity and creates [`MappedComponent`] from it.
fn deserialize_mapped_component(
ctx: &mut WriteCtx,
message: &mut Bytes,
) -> Result<MappedComponent> {
let entity = postcard_utils::from_buf(message)?;
let mut component = MappedComponent {
entity,
unused_field: Default::default(),
};
MappedComponent::map_entities(&mut component, ctx); // Important to call!
Ok(component)
}
#[derive(Component, Deserialize, Serialize)]
struct MappedComponent {
#[entities]
entity: Entity,
unused_field: Vec<bool>,
}
```
Component with [`Box<dyn PartialReflect>`]:
```
use bevy::{
prelude::*,
reflect::serde::{ReflectDeserializer, ReflectSerializer},
};
use bevy_replicon::{
bytes::Bytes,
shared::{
postcard_utils::{BufFlavor, ExtendMutFlavor},
replication::replication_registry::{
ctx::{SerializeCtx, WriteCtx},
rule_fns::RuleFns,
},
},
postcard::{self, Deserializer, Serializer},
prelude::*,
};
use serde::{de::DeserializeSeed, Serialize};
let mut app = App::new();
app.add_plugins(RepliconPlugins);
app.replicate_with(RuleFns::new(serialize_reflect, deserialize_reflect));
fn serialize_reflect(
ctx: &SerializeCtx,
component: &ReflectedComponent,
message: &mut Vec<u8>,
) -> Result<()> {
let mut serializer = Serializer {
output: ExtendMutFlavor::new(message),
};
ReflectSerializer::new(&*component.0, ctx.type_registry).serialize(&mut serializer)?;
Ok(())
}
fn deserialize_reflect(
ctx: &mut WriteCtx,
message: &mut Bytes,
) -> Result<ReflectedComponent> {
let mut deserializer = Deserializer::from_flavor(BufFlavor::new(message));
let reflect = ReflectDeserializer::new(ctx.type_registry).deserialize(&mut deserializer)?;
Ok(ReflectedComponent(reflect))
}
#[derive(Component)]
struct ReflectedComponent(Box<dyn PartialReflect>);
```
*/
fn replicate_with<C>(&mut self, rule_fns: RuleFns<C>) -> &mut Self
where
C: Component<Mutability: MutWrite<C>>;
/**
Creates a replication rule for a group of components.
A group will only be replicated if all its components are present on the entity.
If a group contains a single component, it will work the same as [`Self::replicate`].
If an entity matches multiple groups, functions from a group with higher priority
will take precedence for overlapping components. For example, a rule with `Health`
and a `Player` marker will take precedence over a single `Health` rule.
If you remove a single component from a group, only a single removal will be sent to clients.
Other group components will continue to be present on both server and clients.
Replication for them will be stopped, unless they match other rules.
We provide blanket impls for tuples to replicate them as-is, but a user could manually implement the trait
to customize how components will be serialized and deserialized. For details see [`GroupReplication`].
# Panics
Panics if `debug_assertions` are enabled and any rule is a subset of another.
# Examples
Replicate `Health` and `Player` components only if both of them are present on an entity:
```
use bevy::prelude::*;
use bevy_replicon::prelude::*;
use serde::{Deserialize, Serialize};
# let mut app = App::new();
# app.add_plugins(RepliconPlugins);
app.replicate_group::<(Player, Health)>();
#[derive(Component, Deserialize, Serialize)]
struct Player;
#[derive(Component, Deserialize, Serialize)]
struct Health(u32);
```
**/
fn replicate_group<C: GroupReplication>(&mut self) -> &mut Self;
}
impl AppRuleExt for App {
fn replicate_with<C>(&mut self, rule_fns: RuleFns<C>) -> &mut Self
where
C: Component<Mutability: MutWrite<C>>,
{
let rule =
self.world_mut()
.resource_scope(|world, mut registry: Mut<ReplicationRegistry>| {
let fns_info = registry.register_rule_fns(world, rule_fns);
ReplicationRule::new(vec![fns_info])
});
self.world_mut()
.resource_mut::<ReplicationRules>()
.insert(rule);
self
}
fn replicate_group<C: GroupReplication>(&mut self) -> &mut Self {
let rule =
self.world_mut()
.resource_scope(|world, mut registry: Mut<ReplicationRegistry>| {
C::register(world, &mut registry)
});
self.world_mut()
.resource_mut::<ReplicationRules>()
.insert(rule);
self
}
}
/// All registered rules for components replication.
#[derive(Default, Deref, Resource, Clone)]
pub struct ReplicationRules(Vec<ReplicationRule>);
impl ReplicationRules {
/// Inserts a new rule, maintaining sorting by their priority in descending order.
fn insert(&mut self, rule: ReplicationRule) {
let index = self
.binary_search_by_key(&Reverse(rule.priority), |rule| Reverse(rule.priority))
.unwrap_or_else(|index| index);
self.0.insert(index, rule);
}
}
/// Describes a replicated component or a group of components.
#[derive(Clone)]
pub struct ReplicationRule {
/// Priority for this rule.
///
/// Usually equal to the number of serialized components,
/// but can be adjusted by the user.
pub priority: usize,
/// Rule components and their serialization/deserialization/removal functions.
pub components: Vec<(ComponentId, FnsId)>,
}
impl ReplicationRule {
/// Creates a new rule with priority equal to the number of serializable components.
pub fn new(components: Vec<(ComponentId, FnsId)>) -> Self {
Self {
priority: components.len(),
components,
}
}
/// Determines whether an archetype contains all components required by the rule.
pub(crate) fn matches(&self, archetype: &Archetype) -> bool {
self.components
.iter()
.all(|&(component_id, _)| archetype.contains(component_id))
}
/// Determines whether the rule is applicable to an archetype with removals included and contains at least one removal.
///
/// Returns `true` if all components in this rule are found in either `removed_components` or the
/// `post_removal_archetype`, and at least one component is found in `removed_components`.
/// Returning true means the entity with this archetype satisfied this
/// rule in the previous tick, but then a component within this rule was removed from the entity.
pub(crate) fn matches_removals(
&self,
post_removal_archetype: &Archetype,
removed_components: &HashSet<ComponentId>,
) -> bool {
let mut matches = false;
for &(component_id, _) in &self.components {
if removed_components.contains(&component_id) {
matches = true;
} else if !post_removal_archetype.contains(component_id) {
return false;
}
}
matches
}
}
/**
Describes how a component group should be serialized, deserialized, written, and removed.
Can be implemented on any struct to create a custom replication group.
# Examples
```
# use bevy::prelude::*;
# use bevy_replicon::{
# bytes::Bytes,
# shared::replication::{
# replication_registry::{
# ctx::{SerializeCtx, WriteCtx},
# rule_fns::RuleFns,
# ReplicationRegistry,
# },
# replication_rules::{GroupReplication, ReplicationRule},
# },
# prelude::*,
# };
# use serde::{Deserialize, Serialize};
# let mut app = App::new();
# app.add_plugins(RepliconPlugins);
app.replicate_group::<PlayerBundle>();
#[derive(Bundle)]
struct PlayerBundle {
transform: Transform,
player: Player,
replicated: Replicated,
}
#[derive(Component, Deserialize, Serialize)]
struct Player;
impl GroupReplication for PlayerBundle {
fn register(world: &mut World, registry: &mut ReplicationRegistry) -> ReplicationRule {
// Customize serlialization to serialize only `translation`.
let transform_info = registry.register_rule_fns(
world,
RuleFns::new(serialize_translation, deserialize_translation),
);
// Serialize `player` as usual.
let player_info = registry.register_rule_fns(world, RuleFns::<Player>::default());
// We skip `replication` registration since it's a special component.
// It's automatically inserted on clients after replication and
// deserialization from scenes.
ReplicationRule::new(vec![transform_info, player_info])
}
}
# fn serialize_translation(_: &SerializeCtx, _: &Transform, _: &mut Vec<u8>) -> Result<()> { unimplemented!() }
# fn deserialize_translation(_: &mut WriteCtx, _: &mut Bytes) -> Result<Transform> { unimplemented!() }
```
**/
pub trait GroupReplication {
/// Creates the associated replication rules and registers its functions in [`ReplicationRegistry`].
fn register(world: &mut World, registry: &mut ReplicationRegistry) -> ReplicationRule;
}
macro_rules! impl_registrations {
($($type:ident),*) => {
impl<$($type: Component<Mutability: MutWrite<$type>> + Serialize + DeserializeOwned),*> GroupReplication for ($($type,)*) {
fn register(world: &mut World, registry: &mut ReplicationRegistry) -> ReplicationRule {
// TODO: initialize with capacity after stabilization: https://github.com/rust-lang/rust/pull/122808
let mut components = Vec::new();
$(
let fns_info = registry.register_rule_fns(world, RuleFns::<$type>::default());
components.push(fns_info);
)*
ReplicationRule::new(components)
}
}
}
}
variadics_please::all_tuples!(impl_registrations, 1, 15, B);
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use super::*;
use crate::AppRuleExt;
#[test]
fn sorting() {
let mut app = App::new();
app.init_resource::<ReplicationRules>()
.init_resource::<ReplicationRegistry>()
.replicate::<ComponentA>()
.replicate::<ComponentB>()
.replicate_group::<(ComponentA, ComponentB)>()
.replicate_group::<(ComponentB, ComponentC)>()
.replicate::<ComponentC>()
.replicate::<ComponentD>();
let replication_rules = app.world().resource::<ReplicationRules>();
let priorities: Vec<_> = replication_rules.iter().map(|rule| rule.priority).collect();
assert_eq!(priorities, [2, 2, 1, 1, 1, 1]);
}
#[derive(Serialize, Deserialize, Component)]
struct ComponentA;
#[derive(Serialize, Deserialize, Component)]
struct ComponentB;
#[derive(Serialize, Deserialize, Component)]
struct ComponentC;
#[derive(Serialize, Deserialize, Component)]
struct ComponentD;
}