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

Wrap world access in a SystemParam for granular access #418

Merged
merged 4 commits into from
Feb 14, 2025
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
101 changes: 21 additions & 80 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@ pub mod event;
pub(super) mod removal_buffer;
pub(super) mod replicated_archetypes;
pub(super) mod replication_messages;
mod replication_read_world;
pub mod server_tick;

use std::{mem, ops::Range, time::Duration};
use std::{ops::Range, time::Duration};

use bevy::{
ecs::{
archetype::ArchetypeEntity,
component::{ComponentId, ComponentTicks, StorageType},
storage::{SparseSets, Table},
system::SystemChangeTick,
},
ecs::{component::StorageType, system::SystemChangeTick},
prelude::*,
ptr::Ptr,
time::common_conditions::on_timer,
};
use bytes::Buf;
use replication_read_world::ReplicationReadWorld;

use crate::core::{
channels::{ReplicationChannel, RepliconChannels},
Expand Down Expand Up @@ -256,41 +253,34 @@ pub(super) fn send_replication(
mut messages: Local<ReplicationMessages>,
mut replicated_archetypes: Local<ReplicatedArchetypes>,
change_tick: SystemChangeTick,
mut set: ParamSet<(
&World,
ResMut<ReplicatedClients>,
ResMut<RemovalBuffer>,
ResMut<ClientBuffers>,
ResMut<ClientEntityMap>,
ResMut<DespawnBuffer>,
ResMut<RepliconServer>,
)>,
world: ReplicationReadWorld,
mut replicated_clients: ResMut<ReplicatedClients>,
mut removal_buffer: ResMut<RemovalBuffer>,
mut client_buffers: ResMut<ClientBuffers>,
mut entity_map: ResMut<ClientEntityMap>,
mut despawn_buffer: ResMut<DespawnBuffer>,
mut server: ResMut<RepliconServer>,
track_mutate_messages: Res<TrackMutateMessages>,
registry: Res<ReplicationRegistry>,
rules: Res<ReplicationRules>,
server_tick: Res<ServerTick>,
time: Res<Time>,
) -> postcard::Result<()> {
replicated_archetypes.update(set.p0(), &rules);

// Take ownership to avoid borrowing issues.
let mut replicated_clients = mem::take(&mut *set.p1());
let mut removal_buffer = mem::take(&mut *set.p2());
let mut client_buffers = mem::take(&mut *set.p3());
replicated_archetypes.update(world.archetypes(), world.components(), &rules);

messages.reset(replicated_clients.len());

collect_mappings(
&mut messages,
&mut serialized,
&replicated_clients,
&mut set.p4(),
&mut entity_map,
)?;
collect_despawns(
&mut messages,
&mut serialized,
&mut replicated_clients,
&mut set.p5(),
&mut despawn_buffer,
)?;
collect_removals(
&mut messages,
Expand All @@ -305,7 +295,7 @@ pub(super) fn send_replication(
&replicated_archetypes,
&registry,
&removal_buffer,
set.p0(),
&world,
&change_tick,
**server_tick,
)?;
Expand All @@ -314,7 +304,7 @@ pub(super) fn send_replication(
send_messages(
&mut messages,
&mut replicated_clients,
&mut set.p6(),
&mut server,
**server_tick,
**track_mutate_messages,
&mut serialized,
Expand All @@ -324,11 +314,6 @@ pub(super) fn send_replication(
)?;
serialized.clear();

// Return borrowed data back.
*set.p1() = replicated_clients;
*set.p2() = removal_buffer;
*set.p3() = client_buffers;

Ok(())
}

Expand Down Expand Up @@ -471,7 +456,7 @@ fn collect_changes(
replicated_archetypes: &ReplicatedArchetypes,
registry: &ReplicationRegistry,
removal_buffer: &RemovalBuffer,
world: &World,
world: &ReplicationReadWorld,
change_tick: &SystemChangeTick,
server_tick: RepliconTick,
) -> postcard::Result<()> {
Expand All @@ -483,14 +468,6 @@ fn collect_changes(
.get(replicated_archetype.id)
.unwrap_unchecked()
};
// SAFETY: table obtained from this archetype.
let table = unsafe {
world
.storages()
.tables
.get(archetype.table_id())
.unwrap_unchecked()
};

for entity in archetype.entities() {
let mut entity_range = None;
Expand All @@ -504,10 +481,9 @@ fn collect_changes(

// SAFETY: all replicated archetypes have marker component with table storage.
let (_, marker_ticks) = unsafe {
get_component_unchecked(
table,
&world.storages().sparse_sets,
world.get_component_unchecked(
entity,
archetype.table_id(),
StorageType::Table,
replicated_archetypes.marker_id(),
)
Expand All @@ -524,10 +500,9 @@ fn collect_changes(

// SAFETY: component and storage were obtained from this archetype.
let (component, ticks) = unsafe {
get_component_unchecked(
table,
&world.storages().sparse_sets,
world.get_component_unchecked(
entity,
archetype.table_id(),
replicated_component.storage_type,
component_id,
)
Expand Down Expand Up @@ -623,40 +598,6 @@ fn collect_changes(
Ok(())
}

/// Extracts component in form of [`Ptr`] and its ticks from table or sparse set based on its storage type.
///
/// # Safety
///
/// Component should be present in this archetype and have this storage type.
unsafe fn get_component_unchecked<'w>(
table: &'w Table,
sparse_sets: &'w SparseSets,
entity: &ArchetypeEntity,
storage_type: StorageType,
component_id: ComponentId,
) -> (Ptr<'w>, ComponentTicks) {
match storage_type {
StorageType::Table => {
// TODO: re-use column lookup, asked in https://github.com/bevyengine/bevy/issues/16593.
let component: Ptr<'w> = table
.get_component(component_id, entity.table_row())
.unwrap_unchecked();
let ticks = table
.get_ticks_unchecked(component_id, entity.table_row())
.unwrap_unchecked();

(component, ticks)
}
StorageType::SparseSet => {
let sparse_set = sparse_sets.get(component_id).unwrap_unchecked();
let component = sparse_set.get(entity.id()).unwrap_unchecked();
let ticks = sparse_set.get_ticks(entity.id()).unwrap_unchecked();

(component, ticks)
}
}
}

/// Writes an entity or re-uses previously written range if exists.
fn write_entity_cached(
entity_range: &mut Option<Range<usize>>,
Expand Down
28 changes: 17 additions & 11 deletions src/server/replicated_archetypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::mem;

use bevy::{
ecs::{
archetype::{ArchetypeGeneration, ArchetypeId},
component::{ComponentId, StorageType},
archetype::{ArchetypeGeneration, ArchetypeId, Archetypes},
component::{ComponentId, Components, StorageType},
},
log::Level,
prelude::*,
Expand Down Expand Up @@ -37,11 +37,16 @@ impl ReplicatedArchetypes {
/// Updates the internal view of the [`World`]'s replicated archetypes.
///
/// If this is not called before querying data, the results may not accurately reflect what is in the world.
pub(super) fn update(&mut self, world: &World, rules: &ReplicationRules) {
let old_generation = mem::replace(&mut self.generation, world.archetypes().generation());
pub(super) fn update(
&mut self,
archetypes: &Archetypes,
components: &Components,
rules: &ReplicationRules,
) {
let old_generation = mem::replace(&mut self.generation, archetypes.generation());

// Archetypes are never removed, iterate over newly added since the last update.
for archetype in world.archetypes()[old_generation..]
for archetype in archetypes[old_generation..]
.iter()
.filter(|archetype| archetype.contains(self.marker_id))
{
Expand All @@ -56,17 +61,14 @@ impl ReplicatedArchetypes {
.any(|component| component.component_id == component_id)
{
if enabled!(Level::DEBUG) {
let component_name = world
.components()
let component_name = components
.get_name(component_id)
.expect("rules should be registered with valid component");

let component_names: Vec<_> = replicated_archetype
.components
.iter()
.flat_map(|component| {
world.components().get_name(component.component_id)
})
.flat_map(|component| components.get_name(component.component_id))
.collect();

debug!("ignoring component `{component_name}` with priority {} for archetype with `{component_names:?}`", rule.priority);
Expand Down Expand Up @@ -263,7 +265,11 @@ mod tests {

fn match_archetypes(world: &mut World) -> ReplicatedArchetypes {
let mut archetypes = ReplicatedArchetypes::from_world(world);
archetypes.update(world, world.resource::<ReplicationRules>());
archetypes.update(
world.archetypes(),
world.components(),
world.resource::<ReplicationRules>(),
);

archetypes
}
Expand Down
Loading