Skip to content

Commit

Permalink
Move logic related to replication rules to separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
Shatur committed Sep 30, 2023
1 parent 319b8d7 commit b7d5d91
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 239 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.11.0] - 2023-09-25

- Move logic related to replication rules to `replicon_core::replication_rules` module.

### Changed

- Serialize all components and events using varint.
Expand Down
6 changes: 3 additions & 3 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use bevy_renet::{renet::Bytes, transport::client_connected};
use bevy_renet::{renet::RenetClient, transport::NetcodeClientPlugin, RenetClientPlugin};
use bincode::{DefaultOptions, Options};

use crate::{
replicon_core::{Mapper, NetworkTick, ReplicationRules, REPLICATION_CHANNEL_ID},
Replication,
use crate::replicon_core::{
replication_rules::{Mapper, Replication, ReplicationRules},
NetworkTick, REPLICATION_CHANNEL_ID,
};

pub struct ClientPlugin;
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,11 @@ pub mod prelude {
parent_sync::{ParentSync, ParentSyncPlugin},
renet::{RenetClient, RenetServer},
replicon_core::{
AppReplicationExt, Ignored, MapNetworkEntities, Mapper, NetworkChannels, NetworkTick,
Replication, ReplicationRules, RepliconCorePlugin,
replication_rules::{
AppReplicationExt, Ignored, MapNetworkEntities, Mapper, Replication,
ReplicationRules,
},
NetworkChannels, NetworkTick, RepliconCorePlugin,
},
server::{has_authority, ServerPlugin, ServerSet, ServerTicks, TickPolicy, SERVER_ID},
ReplicationPlugins,
Expand Down
2 changes: 1 addition & 1 deletion src/network_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{marker::PhantomData, time::Duration};
use bevy::{prelude::*, reflect::TypeRegistryInternal, utils::HashMap};
use bevy_renet::renet::SendType;

use crate::replicon_core::Mapper;
use crate::replicon_core::replication_rules::Mapper;

/// Holds a channel ID for `T`.
#[derive(Resource)]
Expand Down
2 changes: 1 addition & 1 deletion src/network_event/client_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use super::{BuildEventDeserializer, BuildEventSerializer, EventChannel};
use crate::{
client::{ClientSet, NetworkEntityMap},
network_event::EventMapper,
replicon_core::{MapNetworkEntities, NetworkChannels},
replicon_core::{replication_rules::MapNetworkEntities, NetworkChannels},
server::{has_authority, ServerSet, SERVER_ID},
};

Expand Down
2 changes: 1 addition & 1 deletion src/network_event/server_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use super::{BuildEventDeserializer, BuildEventSerializer, EventChannel};
use crate::{
client::{ClientSet, NetworkEntityMap},
network_event::EventMapper,
replicon_core::{MapNetworkEntities, NetworkChannels},
replicon_core::{replication_rules::MapNetworkEntities, NetworkChannels},
server::{has_authority, ServerSet, SERVER_ID},
};

Expand Down
3 changes: 1 addition & 2 deletions src/parent_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ use serde::{Deserialize, Serialize};

use crate::{
client::ClientSet,
replicon_core::{AppReplicationExt, Mapper},
replicon_core::replication_rules::{AppReplicationExt, MapNetworkEntities, Mapper},
server::{has_authority, ServerSet},
MapNetworkEntities,
};

pub struct ParentSyncPlugin;
Expand Down
232 changes: 7 additions & 225 deletions src/replicon_core.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use std::{cmp::Ordering, io::Cursor, marker::PhantomData};
pub mod replication_rules;

use bevy::{
ecs::{component::ComponentId, world::EntityMut},
prelude::*,
ptr::Ptr,
utils::HashMap,
};
use bevy_renet::renet::{Bytes, ChannelConfig, SendType};
use bincode::{DefaultOptions, Options};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::cmp::Ordering;

use crate::client::{ClientMapper, NetworkEntityMap};
use bevy::prelude::*;
use bevy_renet::renet::{ChannelConfig, SendType};
use serde::{Deserialize, Serialize};

use replication_rules::ReplicationRules;

pub struct RepliconCorePlugin;

Expand Down Expand Up @@ -78,220 +74,6 @@ fn channel_configs(channels: &[SendType]) -> Vec<ChannelConfig> {
channel_configs
}

pub trait AppReplicationExt {
/// Marks component for replication.
///
/// Component will be serialized as is using bincode.
/// It also registers [`Ignored<T>`] that can be used to exclude the component from replication.
fn replicate<C>(&mut self) -> &mut Self
where
C: Component + Serialize + DeserializeOwned;

/// Same as [`Self::replicate`], but maps component entities using [`MapNetworkEntities`] trait.
///
/// Always use it for components that contains entities.
fn replicate_mapped<C>(&mut self) -> &mut Self
where
C: Component + Serialize + DeserializeOwned + MapNetworkEntities;

/// Same as [`Self::replicate`], but uses the specified functions for serialization and deserialization.
fn replicate_with<C>(
&mut self,
serialize: SerializeFn,
deserialize: DeserializeFn,
) -> &mut Self
where
C: Component;
}

impl AppReplicationExt for App {
fn replicate<C>(&mut self) -> &mut Self
where
C: Component + Serialize + DeserializeOwned,
{
self.replicate_with::<C>(serialize_component::<C>, deserialize_component::<C>)
}

fn replicate_mapped<C>(&mut self) -> &mut Self
where
C: Component + Serialize + DeserializeOwned + MapNetworkEntities,
{
self.replicate_with::<C>(serialize_component::<C>, deserialize_mapped_component::<C>)
}

fn replicate_with<C>(&mut self, serialize: SerializeFn, deserialize: DeserializeFn) -> &mut Self
where
C: Component,
{
let component_id = self.world.init_component::<C>();
let ignored_id = self.world.init_component::<Ignored<C>>();
let replicated_component = ReplicationInfo {
ignored_id,
serialize,
deserialize,
remove: remove_component::<C>,
};

let mut replication_rules = self.world.resource_mut::<ReplicationRules>();
replication_rules.info.push(replicated_component);

let replication_id = ReplicationId(replication_rules.info.len() - 1);
replication_rules.ids.insert(component_id, replication_id);

self
}
}

/// Stores information about which components will be serialized and how.
#[derive(Resource)]
pub struct ReplicationRules {
/// Maps component IDs to their replication IDs.
ids: HashMap<ComponentId, ReplicationId>,

/// Meta information about components that should be replicated.
info: Vec<ReplicationInfo>,

/// ID of [`Replication`] component.
marker_id: ComponentId,
}

impl ReplicationRules {
/// ID of [`Replication`] component, only entities with this components will be replicated.
#[inline]
pub fn get_marker_id(&self) -> ComponentId {
self.marker_id
}

/// Returns ID of the corresponding [`Ignored<T>`] for replicated component.
///
/// Returns [`None`] if the component is not registered for replication.
#[inline]
pub fn ignored_id(&self, component_id: ComponentId) -> Option<ComponentId> {
self.ids
.get(&component_id)
.map(|&replication_id| self.get_info(replication_id).ignored_id)
}

/// Returns mapping of replicated components to their replication IDs.
pub(super) fn get_ids(&self) -> &HashMap<ComponentId, ReplicationId> {
&self.ids
}

/// Returns meta information about replicated component.
#[inline]
pub(super) fn get_info(&self, replication_id: ReplicationId) -> &ReplicationInfo {
// SAFETY: `ReplicationId` always corresponds to a valid index.
unsafe { self.info.get_unchecked(replication_id.0) }
}
}

impl FromWorld for ReplicationRules {
fn from_world(world: &mut World) -> Self {
Self {
info: Default::default(),
ids: Default::default(),
marker_id: world.init_component::<Replication>(),
}
}
}

/// Signature of component serialization functions.
pub type SerializeFn = fn(Ptr, &mut Cursor<Vec<u8>>) -> Result<(), bincode::Error>;

/// Signature of component deserialization functions.
pub type DeserializeFn =
fn(&mut EntityMut, &mut NetworkEntityMap, &mut Cursor<Bytes>) -> Result<(), bincode::Error>;

pub(super) struct ReplicationInfo {
/// ID of [`Ignored<T>`] component.
pub(super) ignored_id: ComponentId,

/// Function that serializes component into bytes.
pub(super) serialize: SerializeFn,

/// Function that deserializes component from bytes and inserts it to [`EntityMut`].
pub(super) deserialize: DeserializeFn,

/// Function that removes specific component from [`EntityMut`].
pub(super) remove: fn(&mut EntityMut),
}

/// Replication will be ignored for `T` if this component is present on the same entity.
#[derive(Component)]
pub struct Ignored<T>(PhantomData<T>);

impl<T> Default for Ignored<T> {
fn default() -> Self {
Self(PhantomData)
}
}

/// Same as [`ComponentId`], but consistent between server and clients.
///
/// Internally represents index of [`ReplicationInfo`].
#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub(super) struct ReplicationId(usize);

/// Default serialization function.
fn serialize_component<C: Component + Serialize>(
component: Ptr,
cursor: &mut Cursor<Vec<u8>>,
) -> Result<(), bincode::Error> {
// SAFETY: Function called for registered `ComponentId`.
let component: &C = unsafe { component.deref() };
DefaultOptions::new().serialize_into(cursor, component)
}

/// Default deserialization function.
fn deserialize_component<C: Component + DeserializeOwned>(
entity: &mut EntityMut,
_entity_map: &mut NetworkEntityMap,
cursor: &mut Cursor<Bytes>,
) -> Result<(), bincode::Error> {
let component: C = DefaultOptions::new().deserialize_from(cursor)?;
entity.insert(component);

Ok(())
}

/// Like [`deserialize_component`], but also maps entities before insertion.
fn deserialize_mapped_component<C: Component + DeserializeOwned + MapNetworkEntities>(
entity: &mut EntityMut,
entity_map: &mut NetworkEntityMap,
cursor: &mut Cursor<Bytes>,
) -> Result<(), bincode::Error> {
let mut component: C = DefaultOptions::new().deserialize_from(cursor)?;

entity.world_scope(|world| {
component.map_entities(&mut ClientMapper::new(world, entity_map));
});

entity.insert(component);

Ok(())
}

/// Removes specified component from entity.
fn remove_component<C: Component>(entity: &mut EntityMut) {
entity.remove::<C>();
}

/// Maps entities inside component.
///
/// The same as [`bevy::ecs::entity::MapEntities`], but never creates new entities on mapping error.
pub trait MapNetworkEntities {
/// Maps stored entities using specified map.
fn map_entities<T: Mapper>(&mut self, mapper: &mut T);
}

pub trait Mapper {
fn map(&mut self, entity: Entity) -> Entity;
}

/// Marks entity for replication.
#[derive(Component, Clone, Copy)]
pub struct Replication;

/// Corresponds to the number of server update.
///
/// See also [`crate::server::TickPolicy`].
Expand Down
Loading

0 comments on commit b7d5d91

Please sign in to comment.