Skip to content

Refactoring in preparation for manual packet fragmentation #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Use `EntityHashMap` instead of `HashMap` with entities as keys.
- Rename `replicate_into_scene` into `replicate_into` and move it to `scene` module.

## [0.17.0] - 2023-11-13

### Added
Expand Down
24 changes: 12 additions & 12 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::Cursor;

use bevy::{
prelude::*,
utils::{Entry, HashMap},
utils::{hashbrown::hash_map::Entry, EntityHashMap},
};
use bevy_renet::{client_connected, renet::Bytes};
use bevy_renet::{renet::RenetClient, transport::NetcodeClientPlugin, RenetClientPlugin};
Expand Down Expand Up @@ -171,8 +171,8 @@ fn apply_entity_mappings(
stats.mappings += array_len as u32;
}
for _ in 0..array_len {
let server_entity = read_entity(cursor)?;
let client_entity = read_entity(cursor)?;
let server_entity = deserialize_entity(cursor)?;
let client_entity = deserialize_entity(cursor)?;

if let Some(entry) = entity_map.to_client().get(&server_entity) {
// It's possible to receive the same mappings in multiple packets if the server has not
Expand Down Expand Up @@ -206,7 +206,7 @@ fn apply_components(
) -> bincode::Result<()> {
let entities_count: u16 = bincode::deserialize_from(&mut *cursor)?;
for _ in 0..entities_count {
let entity = read_entity(cursor)?;
let entity = deserialize_entity(cursor)?;
let mut entity = entity_map.get_by_server_or_spawn(world, entity);
let components_count: u8 = bincode::deserialize_from(&mut *cursor)?;
if let Some(stats) = &mut stats {
Expand Down Expand Up @@ -246,7 +246,7 @@ fn apply_despawns(
// The entity might have already been despawned because of hierarchy or
// with the last replication message, but the server might not yet have received confirmation
// from the client and could include the deletion in the this message.
let server_entity = read_entity(cursor)?;
let server_entity = deserialize_entity(cursor)?;
if let Some(client_entity) = entity_map
.remove_by_server(server_entity)
.and_then(|entity| world.get_entity_mut(entity))
Expand All @@ -261,7 +261,7 @@ fn apply_despawns(
/// Deserializes `entity` from compressed index and generation.
///
/// For details see [`ReplicationBuffer::write_entity`](crate::server::replication_buffer::ReplicationBuffer::write_entity).
fn read_entity(cursor: &mut Cursor<Bytes>) -> bincode::Result<Entity> {
fn deserialize_entity(cursor: &mut Cursor<Bytes>) -> bincode::Result<Entity> {
let flagged_index: u64 = cursor.read_u64_varint()?;
let has_generation = (flagged_index & 1) > 0;
let generation = if has_generation {
Expand Down Expand Up @@ -307,8 +307,8 @@ pub enum ClientSet {
/// Used only on client.
#[derive(Default, Resource)]
pub struct ServerEntityMap {
server_to_client: HashMap<Entity, Entity>,
client_to_server: HashMap<Entity, Entity>,
server_to_client: EntityHashMap<Entity, Entity>,
client_to_server: EntityHashMap<Entity, Entity>,
}

impl ServerEntityMap {
Expand Down Expand Up @@ -344,12 +344,12 @@ impl ServerEntityMap {
}

#[inline]
pub fn to_client(&self) -> &HashMap<Entity, Entity> {
pub fn to_client(&self) -> &EntityHashMap<Entity, Entity> {
&self.server_to_client
}

#[inline]
pub fn to_server(&self) -> &HashMap<Entity, Entity> {
pub fn to_server(&self) -> &EntityHashMap<Entity, Entity> {
&self.client_to_server
}

Expand All @@ -364,8 +364,8 @@ impl ServerEntityMap {
/// Spawns new client entity if a mapping doesn't exists.
pub struct ClientMapper<'a> {
world: &'a mut World,
server_to_client: &'a mut HashMap<Entity, Entity>,
client_to_server: &'a mut HashMap<Entity, Entity>,
server_to_client: &'a mut EntityHashMap<Entity, Entity>,
client_to_server: &'a mut EntityHashMap<Entity, Entity>,
}

impl<'a> ClientMapper<'a> {
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ struct Player;
```

This pairs nicely with server state serialization and keeps saves clean.
You can use [`replicate_into_scene`](server::replicate_into_scene) to
You can use [`replicate_into`](scene::replicate_into) to
fill `DynamicScene` with replicated entities and their components.

The mentioned [`Ignored<T>`] component is an exception.
Expand Down Expand Up @@ -388,6 +388,7 @@ pub mod client;
pub mod network_event;
pub mod parent_sync;
pub mod replicon_core;
pub mod scene;
pub mod server;

pub mod prelude {
Expand Down
4 changes: 2 additions & 2 deletions src/network_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod server_event;

use std::{marker::PhantomData, time::Duration};

use bevy::{prelude::*, utils::HashMap};
use bevy::{prelude::*, utils::EntityHashMap};
use bevy_renet::renet::SendType;

use crate::replicon_core::replication_rules::Mapper;
Expand Down Expand Up @@ -70,7 +70,7 @@ impl From<EventType> for SendType {
/// Maps server entities into client entities inside events.
///
/// Panics if a mapping doesn't exists.
pub struct EventMapper<'a>(pub &'a HashMap<Entity, Entity>);
pub struct EventMapper<'a>(pub &'a EntityHashMap<Entity, Entity>);

impl Mapper for EventMapper<'_> {
fn map(&mut self, entity: Entity) -> Entity {
Expand Down
102 changes: 102 additions & 0 deletions src/scene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use bevy::{ecs::archetype::ArchetypeId, prelude::*, scene::DynamicEntity};

use crate::replicon_core::replication_rules::ReplicationRules;

/**
Fills scene with all replicated entities and their components.

Entities won't have the [`Replication`](crate::replicon_core::replication_rules::Replication) component.
So on deserialization you need to insert it back if you want entities to continue to replicate.

# Panics

Panics if any replicated component is not registered using `register_type()`
or `#[reflect(Component)]` is missing.

# Examples

```
use bevy::{prelude::*, scene::serde::SceneDeserializer};
use bevy_replicon::{prelude::*, scene};
use serde::de::DeserializeSeed;
# let mut world = World::new();
# world.init_resource::<AppTypeRegistry>();
# world.init_resource::<ReplicationRules>();

// Serialization
let registry = world.resource::<AppTypeRegistry>();
let mut scene = DynamicScene::default();
scene::replicate_into(&mut scene, &world);
let scene = scene
.serialize_ron(&registry)
.expect("scene should be serialized");

// Deserialization
let scene_deserializer = SceneDeserializer {
type_registry: &registry.read(),
};
let mut deserializer =
ron::Deserializer::from_str(&scene).expect("scene should be serialized as valid ron");
let mut scene = scene_deserializer
.deserialize(&mut deserializer)
.expect("ron should be convertible to scene");

// All saved entities should have `Replication` component.
for entity in &mut scene.entities {
entity.components.push(Replication.clone_value());
}
```
*/
pub fn replicate_into(scene: &mut DynamicScene, world: &World) {
let registry = world.resource::<AppTypeRegistry>();
let replication_rules = world.resource::<ReplicationRules>();

let registry = registry.read();
for archetype in world
.archetypes()
.iter()
.filter(|archetype| archetype.id() != ArchetypeId::EMPTY)
.filter(|archetype| archetype.id() != ArchetypeId::INVALID)
.filter(|archetype| archetype.contains(replication_rules.get_marker_id()))
{
let entities_offset = scene.entities.len();
for archetype_entity in archetype.entities() {
scene.entities.push(DynamicEntity {
entity: archetype_entity.entity(),
components: Vec::new(),
});
}

for component_id in archetype.components() {
let Some((_, replication_info)) = replication_rules.get(component_id) else {
continue;
};
if archetype.contains(replication_info.ignored_id) {
continue;
}

// SAFETY: `component_info` obtained from the world.
let component_info = unsafe { world.components().get_info_unchecked(component_id) };
let type_name = component_info.name();
let type_id = component_info
.type_id()
.unwrap_or_else(|| panic!("{type_name} should have registered TypeId"));
let registration = registry
.get(type_id)
.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)"));

for (index, archetype_entity) in archetype.entities().iter().enumerate() {
let component = reflect_component
.reflect(world.entity(archetype_entity.entity()))
.unwrap_or_else(|| panic!("entity should have {type_name}"));

scene.entities[entities_offset + index]
.components
.push(component.clone_value());
}
}
}
}
Loading