Skip to content

Commit

Permalink
Replace reflection with plain serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Shatur committed Sep 12, 2023
1 parent 44d10d3 commit fba0e65
Show file tree
Hide file tree
Showing 15 changed files with 507 additions and 790 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- `MapEventEntities` was renamed into `MapNetworkEntities` and now used for both components and events. Built-in `MapEntities` trait from Bevy is not suited for network case for now.
- `AppReplicationExt::not_replicate_with` was replaced with component marker `Ignored<T>`.
- Reflection was replaced with plain serialization. Now components need implement serde traits and no longer need any reflection. This reduced reduced message sizes a lot. Because of this mapped components now need to be registered with `AppReplicationExt::replicate_mapped`.
- Derive `Clone` and `Copy` for `Replication`.
- Make `ServerPlugin` fields private and add `ServerPlugin::new`.
- Make `AckedTicks` public.
Expand Down
4 changes: 2 additions & 2 deletions benches/replication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::time::{Duration, Instant};
use bevy::{app::MainScheduleOrder, ecs::schedule::ExecutorKind, prelude::*};
use bevy_replicon::prelude::*;
use criterion::{criterion_group, criterion_main, Criterion};
use serde::{Deserialize, Serialize};

#[derive(Component, Reflect, Default, Clone, Copy)]
#[reflect(Component)]
#[derive(Component, Clone, Copy, Serialize, Deserialize)]
struct DummyComponent;

const ENTITIES: u32 = 900;
Expand Down
8 changes: 2 additions & 6 deletions examples/tic_tac_toe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,8 @@ struct CurrentTurn(Symbol);
PartialEq,
Serialize,
ValueEnum,
Reflect,
)]
#[strum(serialize_all = "kebab-case")]
#[reflect(Component)]
enum Symbol {
#[default]
Cross,
Expand Down Expand Up @@ -655,8 +653,7 @@ impl SymbolBundle {
}

/// Marks that the entity is a cell and contains its location in grid.
#[derive(Component, Default, Deserialize, Reflect, Serialize)]
#[reflect(Component)]
#[derive(Component, Deserialize, Serialize)]
struct CellIndex(usize);

/// Contains player ID and it's playing symbol.
Expand All @@ -682,8 +679,7 @@ impl PlayerBundle {
}
}

#[derive(Component, Reflect, Default)]
#[reflect(Component)]
#[derive(Component, Serialize, Deserialize)]
struct Player(u64);

/// An event that indicates a symbol pick.
Expand Down
99 changes: 32 additions & 67 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
use bevy::{
ecs::{component::Tick, entity::EntityMap, reflect::ReflectMapEntities, system::Command},
ecs::{component::Tick, system::Command},
prelude::*,
reflect::TypeRegistryInternal,
utils::HashMap,
};
use bevy_renet::transport::client_connected;
use bevy_renet::{renet::RenetClient, transport::NetcodeClientPlugin, RenetClientPlugin};
use bincode::{DefaultOptions, Options};
use serde::{de::DeserializeSeed, Deserialize, Serialize};
use serde::{Deserialize, Serialize};

use crate::{
replication_core::REPLICATION_CHANNEL_ID,
world_diff::{ComponentDiff, WorldDiff, WorldDiffDeserializer},
prelude::ReplicationRules,
replication_core::{ComponentDiff, WorldDiff, REPLICATION_CHANNEL_ID},
Replication,
};

Expand Down Expand Up @@ -53,23 +51,14 @@ impl ClientPlugin {
mut commands: Commands,
mut last_tick: ResMut<LastTick>,
mut client: ResMut<RenetClient>,
registry: Res<AppTypeRegistry>,
) {
let mut last_message = None;
while let Some(message) = client.receive_message(REPLICATION_CHANNEL_ID) {
last_message = Some(message);
}

if let Some(last_message) = last_message {
let registry = registry.read();
// Set options to match `bincode::serialize`.
// https://docs.rs/bincode/latest/bincode/config/index.html#options-struct-vs-bincode-functions
let options = DefaultOptions::new()
.with_fixint_encoding()
.allow_trailing_bytes();
let mut deserializer = bincode::Deserializer::from_slice(&last_message, options);
let world_diff = WorldDiffDeserializer::new(&registry)
.deserialize(&mut deserializer)
let world_diff: WorldDiff = bincode::deserialize(&last_message)
.expect("server should send only world diffs over replication channel");
*last_tick = world_diff.tick.into();
commands.apply_world_diff(world_diff);
Expand Down Expand Up @@ -122,21 +111,29 @@ struct ApplyWorldDiff(WorldDiff);

impl Command for ApplyWorldDiff {
fn apply(self, world: &mut World) {
let registry = world.resource::<AppTypeRegistry>().clone();
let registry = registry.read();
world.resource_scope(|world, mut entity_map: Mut<NetworkEntityMap>| {
// Map entities non-lazily in order to correctly map components that reference server entities.
for (entity, components) in map_entities(world, &mut entity_map, self.0.entities) {
for component_diff in components {
apply_component_diff(
world,
&mut entity_map,
&registry,
entity,
&component_diff,
);
world.resource_scope(|world, replication_rules: Mut<ReplicationRules>| {
// Map entities non-lazily in order to correctly map components that reference server entities.
for (entity, components) in map_entities(world, &mut entity_map, self.0.entities) {
let mut entity = world.entity_mut(entity);
for component_diff in components {
match component_diff {
ComponentDiff::Changed((replication_id, component)) => {
let replication_info = replication_rules.get_info(replication_id);
(replication_info.deserialize)(
&mut entity,
&entity_map.server_to_client,
&component,
);
}
ComponentDiff::Removed(replication_id) => {
let replication_info = replication_rules.get_info(replication_id);
(replication_info.remove)(&mut entity);
}
}
}
}
}
});

for server_entity in self.0.despawns {
// The entity might have already been deleted with the last diff,
Expand Down Expand Up @@ -164,38 +161,6 @@ fn map_entities(
mapped_entities
}

fn apply_component_diff(
world: &mut World,
entity_map: &mut NetworkEntityMap,
registry: &TypeRegistryInternal,
client_entity: Entity,
component_diff: &ComponentDiff,
) {
let type_name = component_diff.type_name();
let registration = registry
.get_with_name(type_name)
.unwrap_or_else(|| panic!("{type_name} should be registered"));

let reflect_component = registration
.data::<ReflectComponent>()
.unwrap_or_else(|| panic!("{type_name} should have reflect(Component)"));

match component_diff {
ComponentDiff::Changed(component) => {
reflect_component.apply_or_insert(&mut world.entity_mut(client_entity), &**component);
if let Some(reflect_map_entities) = registration.data::<ReflectMapEntities>() {
// TODO 0.12: Remove mutable access.
reflect_map_entities.map_entities(
world,
&mut entity_map.server_to_client,
&[client_entity],
);
}
}
ComponentDiff::Removed(_) => reflect_component.remove(&mut world.entity_mut(client_entity)),
}
}

/// Set with replication and event systems related to client.
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum ClientSet {
Expand All @@ -214,8 +179,8 @@ pub enum ClientSet {
/// Used only on client.
#[derive(Default, Resource)]
pub struct NetworkEntityMap {
server_to_client: EntityMap,
client_to_server: EntityMap,
server_to_client: HashMap<Entity, Entity>,
client_to_server: HashMap<Entity, Entity>,
}

impl NetworkEntityMap {
Expand All @@ -236,18 +201,18 @@ impl NetworkEntityMap {
}

fn remove_by_server(&mut self, server_entity: Entity) -> Option<Entity> {
let client_entity = self.server_to_client.remove(server_entity);
let client_entity = self.server_to_client.remove(&server_entity);
if let Some(client_entity) = client_entity {
self.client_to_server.remove(client_entity);
self.client_to_server.remove(&client_entity);
}
client_entity
}

pub fn to_client(&self) -> &EntityMap {
pub fn to_client(&self) -> &HashMap<Entity, Entity> {
&self.server_to_client
}

pub fn to_server(&self) -> &EntityMap {
pub fn to_server(&self) -> &HashMap<Entity, Entity> {
&self.client_to_server
}
}
Loading

0 comments on commit fba0e65

Please sign in to comment.