Skip to content

Commit

Permalink
Replication rules rework (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shatur authored Sep 22, 2023
1 parent 6ec2441 commit 5bd125a
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 29 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

### Changed

- Hide `ReplicationId`, `ReplicationInfo` and related methods from `ReplicationRules` from public API.
- Rename `ReplicationRules::replication_id` into `ReplicationRules::replication_marker_id`.
- Use serialization buffer cache per client for replication.
- Correctly handle old values on packet reordering.
- Bevy's `Tick` was replaced with dedicated type `NetworkTick` that increments on server update, so it can be used to provide information about time. `AckedTick` was replaced with `ServerTicks` that also contains mappings from `NetworkTick` to Bevy's `Tick` and current `NetworkTick`.
Expand Down
56 changes: 31 additions & 25 deletions src/replicon_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,7 @@ impl AppReplicationExt for App {
replication_rules.info.push(replicated_component);

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

self
}
Expand All @@ -147,67 +145,75 @@ impl AppReplicationExt for App {
#[derive(Resource)]
pub struct ReplicationRules {
/// Maps component IDs to their replication IDs.
components: HashMap<ComponentId, ReplicationId>,
ids: HashMap<ComponentId, ReplicationId>,

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

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

impl ReplicationRules {
/// ID of [`Replication`] component, only entities with this components will be replicated.
pub fn replication_id(&self) -> ComponentId {
self.replication_id
#[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 fn components(&self) -> &HashMap<ComponentId, ReplicationId> {
&self.components
#[inline]
pub(super) fn get_ids(&self) -> &HashMap<ComponentId, ReplicationId> {
&self.ids
}

/// Returns meta information about replicated component.
pub fn get_info(&self, replication_id: ReplicationId) -> &ReplicationInfo {
#[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) }
}

/// Returns ID for component that will be consistent between clients and server.
pub fn get_id(&self, component_id: ComponentId) -> Option<ReplicationId> {
self.components.get(&component_id).copied()
}
}

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

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

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

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

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

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

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

/// Replication will be ignored for `T` if this component is present on the same entity.
Expand All @@ -224,7 +230,7 @@ impl<T> Default for Ignored<T> {
///
/// Internally represents index of [`ReplicationInfo`].
#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ReplicationId(usize);
pub(super) struct ReplicationId(usize);

/// Default serialization function.
fn serialize_component<C: Component + Serialize>(
Expand Down
4 changes: 2 additions & 2 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ fn collect_changes(
.iter()
.filter(|archetype| archetype.id() != ArchetypeId::EMPTY)
.filter(|archetype| archetype.id() != ArchetypeId::INVALID)
.filter(|archetype| archetype.contains(replication_rules.replication_id()))
.filter(|archetype| archetype.contains(replication_rules.get_marker_id()))
{
let table = world
.storages()
Expand All @@ -226,7 +226,7 @@ fn collect_changes(
}

for component_id in archetype.components() {
let Some(replication_id) = replication_rules.get_id(component_id) else {
let Some(&replication_id) = replication_rules.get_ids().get(&component_id) else {
continue;
};
let replication_info = replication_rules.get_info(replication_id);
Expand Down
4 changes: 2 additions & 2 deletions src/server/removal_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl RemovalTrackerPlugin {
replication_rules: Res<ReplicationRules>,
mut removal_trackers: Query<&mut RemovalTracker>,
) {
for (&component_id, &replication_id) in replication_rules.components() {
for (&component_id, &replication_id) in replication_rules.get_ids() {
for entity in remove_events
.get(component_id)
.map(|removed| removed.iter_current_update_events().cloned())
Expand Down Expand Up @@ -119,7 +119,7 @@ mod tests {

let component_id = app.world.init_component::<DummyComponent>();
let replcation_rules = app.world.resource::<ReplicationRules>();
let replication_id = replcation_rules.get_id(component_id).unwrap();
let replication_id = *replcation_rules.get_ids().get(&component_id).unwrap();
let removal_tracker = app.world.get::<RemovalTracker>(replicated_entity).unwrap();
assert!(removal_tracker.contains_key(&replication_id));
}
Expand Down

0 comments on commit 5bd125a

Please sign in to comment.