Skip to content
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

Move replicate_into_scene to server module #64

Merged
merged 1 commit into from
Oct 1, 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
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ struct Player;
```

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

### Component relations
Expand Down
108 changes: 1 addition & 107 deletions src/replicon_core/replication_rules.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::{io::Cursor, marker::PhantomData};

use bevy::{
ecs::{archetype::ArchetypeId, component::ComponentId, world::EntityMut},
ecs::{component::ComponentId, world::EntityMut},
prelude::*,
ptr::Ptr,
scene::DynamicEntity,
utils::HashMap,
};
use bevy_renet::renet::Bytes;
Expand Down Expand Up @@ -235,108 +234,3 @@ fn deserialize_mapped_component<C: Component + DeserializeOwned + MapNetworkEnti
fn remove_component<C: Component>(entity: &mut EntityMut) {
entity.remove::<C>();
}

/// Fills scene with all replicated entities and their components.
///
/// # Panics
///
/// Panics if any replicated component is not registered using `register_type()`
/// or missing `#[reflect(Component)]`.
pub fn replicate_into_scene(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.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());
}
}
}
}

#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};

use super::*;

#[test]
fn replication_into_scene() {
let mut app = App::new();
app.init_resource::<ReplicationRules>()
.register_type::<DummyComponent>()
.replicate::<DummyComponent>();

app.world.spawn(DummyComponent);
let dummy_entity = app.world.spawn((Replication, DummyComponent)).id();
let empty_entity = app
.world
.spawn((
Replication,
DummyComponent,
Ignored::<DummyComponent>::default(),
))
.id();

let mut scene = DynamicScene::default();
replicate_into_scene(&mut scene, &app.world);

assert!(scene.resources.is_empty());

let [dummy, empty] = &scene.entities[..] else {
panic!("scene should only contain entities marked for replication");
};

assert_eq!(dummy.entity, dummy_entity);
assert_eq!(dummy.components.len(), 1);

assert_eq!(empty.entity, empty_entity);
assert!(empty.components.is_empty());
}

#[derive(Component, Default, Deserialize, Reflect, Serialize)]
#[reflect(Component)]
struct DummyComponent;
}
61 changes: 61 additions & 0 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use bevy::{
},
prelude::*,
ptr::Ptr,
scene::DynamicEntity,
time::common_conditions::on_timer,
utils::HashMap,
};
Expand Down Expand Up @@ -668,3 +669,63 @@ impl ReplicationBuffer {
Ok(())
}
}

/// Fills scene with all replicated entities and their components.
///
/// # Panics
///
/// Panics if any replicated component is not registered using `register_type()`
/// or missing `#[reflect(Component)]`.
pub fn replicate_into_scene(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());
}
}
}
}
40 changes: 39 additions & 1 deletion tests/replication.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod common;

use bevy::prelude::*;
use bevy_replicon::prelude::*;
use bevy_replicon::{prelude::*, server};

use bevy_renet::renet::transport::NetcodeClientTransport;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -237,6 +237,40 @@ fn despawn_replication() {
assert!(entity_map.to_server().is_empty());
}

#[test]
fn replication_into_scene() {
let mut app = App::new();
app.add_plugins(ReplicationPlugins)
.register_type::<ReflectedComponent>()
.replicate::<ReflectedComponent>();

app.world.spawn(ReflectedComponent);
let reflect_entity = app.world.spawn((Replication, ReflectedComponent)).id();
let empty_entity = app
.world
.spawn((
Replication,
ReflectedComponent,
Ignored::<ReflectedComponent>::default(),
))
.id();

let mut scene = DynamicScene::default();
server::replicate_into_scene(&mut scene, &app.world);

assert!(scene.resources.is_empty());

let [reflect, empty] = &scene.entities[..] else {
panic!("scene should only contain entities marked for replication");
};

assert_eq!(reflect.entity, reflect_entity);
assert_eq!(reflect.components.len(), 1);

assert_eq!(empty.entity, empty_entity);
assert!(empty.components.is_empty());
}

#[derive(Component, Deserialize, Serialize)]
struct MappedComponent(Entity);

Expand All @@ -258,3 +292,7 @@ struct NonReplicatingComponent;

#[derive(Component, Deserialize, Serialize)]
struct IgnoredComponent;

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