Sync some components only from the client's own entity. Vastly reduce the amount of places to modify when adding/removing a component from network syncing.

This commit is contained in:
Imbris 2022-01-18 00:24:37 -05:00
parent c4706078c4
commit aa122c1a18
11 changed files with 596 additions and 638 deletions

View File

@ -2,3 +2,4 @@
#![feature(generic_const_exprs, const_fn_floating_point_arithmetic)]
pub mod msg;
pub mod sync;
pub mod synced_components;

View File

@ -1,224 +1,86 @@
use crate::sync;
use common::{
comp,
link::Is,
mounting::{Mount, Rider},
resources::Time,
};
use common::comp;
use serde::{Deserialize, Serialize};
use specs::WorldExt;
use std::marker::PhantomData;
use sum_type::sum_type;
// Automatically derive From<T> for EcsCompPacket
// for each variant EcsCompPacket::T(T.)
sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPacket {
Body(comp::Body),
Player(comp::Player),
CanBuild(comp::CanBuild),
Stats(comp::Stats),
SkillSet(comp::SkillSet),
ActiveAbilities(comp::ActiveAbilities),
Buffs(comp::Buffs),
Auras(comp::Auras),
Energy(comp::Energy),
Combo(comp::Combo),
Health(comp::Health),
Poise(comp::Poise),
LightEmitter(comp::LightEmitter),
Inventory(comp::Inventory),
Item(comp::Item),
Scale(comp::Scale),
Group(comp::Group),
IsMount(Is<Mount>),
IsRider(Is<Rider>),
Mass(comp::Mass),
Density(comp::Density),
Collider(comp::Collider),
Sticky(comp::Sticky),
CharacterState(comp::CharacterState),
Pos(comp::Pos),
Vel(comp::Vel),
Ori(comp::Ori),
Shockwave(comp::Shockwave),
BeamSegment(comp::BeamSegment),
Alignment(comp::Alignment),
}
}
// Automatically derive From<T> for EcsCompPhantom
// for each variant EcsCompPhantom::T(PhantomData<T>).
sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPhantom {
Body(PhantomData<comp::Body>),
Player(PhantomData<comp::Player>),
CanBuild(PhantomData<comp::CanBuild>),
Stats(PhantomData<comp::Stats>),
SkillSet(PhantomData<comp::SkillSet>),
ActiveAbilities(PhantomData<comp::ActiveAbilities>),
Buffs(PhantomData<comp::Buffs>),
Auras(PhantomData<comp::Auras>),
Energy(PhantomData<comp::Energy>),
Combo(PhantomData<comp::Combo>),
Health(PhantomData<comp::Health>),
Poise(PhantomData<comp::Poise>),
LightEmitter(PhantomData<comp::LightEmitter>),
Inventory(PhantomData<comp::Inventory>),
Item(PhantomData<comp::Item>),
Scale(PhantomData<comp::Scale>),
Group(PhantomData<comp::Group>),
IsMount(PhantomData<Is<Mount>>),
IsRider(PhantomData<Is<Rider>>),
Mass(PhantomData<comp::Mass>),
Density(PhantomData<comp::Density>),
Collider(PhantomData<comp::Collider>),
Sticky(PhantomData<comp::Sticky>),
CharacterState(PhantomData<comp::CharacterState>),
Pos(PhantomData<comp::Pos>),
Vel(PhantomData<comp::Vel>),
Ori(PhantomData<comp::Ori>),
Shockwave(PhantomData<comp::Shockwave>),
BeamSegment(PhantomData<comp::BeamSegment>),
Alignment(PhantomData<comp::Alignment>),
}
}
impl sync::CompPacket for EcsCompPacket {
type Phantom = EcsCompPhantom;
fn apply_insert(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
match self {
EcsCompPacket::Body(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Player(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Stats(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::SkillSet(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::ActiveAbilities(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Buffs(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Auras(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Energy(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Combo(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Health(mut comp) => {
// Time isn't synced between client and server so replace the Time from the
// server with the Client's local Time to enable accurate comparison.
comp.last_change.time = *world.read_resource::<Time>();
sync::handle_insert(comp, entity, world)
},
EcsCompPacket::Poise(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Inventory(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Group(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::IsMount(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::IsRider(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Density(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Collider(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Pos(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
EcsCompPacket::Vel(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
EcsCompPacket::Ori(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
EcsCompPacket::Shockwave(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::BeamSegment(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Alignment(comp) => sync::handle_insert(comp, entity, world),
macro_rules! comp_packet {
($($component_name:ident: $component_type:ident,)*) => {
sum_type::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPacket {
$($component_type($component_type),)*
Pos(comp::Pos),
Vel(comp::Vel),
Ori(comp::Ori),
}
}
}
fn apply_modify(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
match self {
EcsCompPacket::Body(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Player(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::CanBuild(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Stats(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::SkillSet(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::ActiveAbilities(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Buffs(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Auras(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Energy(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Combo(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Health(mut comp) => {
// Time isn't synced between client and server so replace the Time from the
// server with the Client's local Time to enable accurate comparison.
comp.last_change.time = *world.read_resource::<Time>();
sync::handle_modify(comp, entity, world)
},
EcsCompPacket::Poise(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Inventory(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Group(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::IsMount(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::IsRider(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Density(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Collider(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Pos(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
EcsCompPacket::Vel(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
EcsCompPacket::Ori(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
EcsCompPacket::Shockwave(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::BeamSegment(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Alignment(comp) => sync::handle_modify(comp, entity, world),
sum_type::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPhantom {
$($component_type(PhantomData<$component_type>),)*
Pos(PhantomData<comp::Pos>),
Vel(PhantomData<comp::Vel>),
Ori(PhantomData<comp::Ori>),
}
}
}
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
match phantom {
EcsCompPhantom::Body(_) => sync::handle_remove::<comp::Body>(entity, world),
EcsCompPhantom::Player(_) => sync::handle_remove::<comp::Player>(entity, world),
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
EcsCompPhantom::Stats(_) => sync::handle_remove::<comp::Stats>(entity, world),
EcsCompPhantom::SkillSet(_) => sync::handle_remove::<comp::SkillSet>(entity, world),
EcsCompPhantom::ActiveAbilities(_) => {
sync::handle_remove::<comp::ActiveAbilities>(entity, world)
},
EcsCompPhantom::Buffs(_) => sync::handle_remove::<comp::Buffs>(entity, world),
EcsCompPhantom::Auras(_) => sync::handle_remove::<comp::Auras>(entity, world),
EcsCompPhantom::Energy(_) => sync::handle_remove::<comp::Energy>(entity, world),
EcsCompPhantom::Combo(_) => sync::handle_remove::<comp::Combo>(entity, world),
EcsCompPhantom::Health(_) => sync::handle_remove::<comp::Health>(entity, world),
EcsCompPhantom::Poise(_) => sync::handle_remove::<comp::Poise>(entity, world),
EcsCompPhantom::LightEmitter(_) => {
sync::handle_remove::<comp::LightEmitter>(entity, world)
},
EcsCompPhantom::Inventory(_) => sync::handle_remove::<comp::Inventory>(entity, world),
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),
EcsCompPhantom::Group(_) => sync::handle_remove::<comp::Group>(entity, world),
EcsCompPhantom::IsMount(_) => sync::handle_remove::<Is<Mount>>(entity, world),
EcsCompPhantom::IsRider(_) => sync::handle_remove::<Is<Rider>>(entity, world),
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
EcsCompPhantom::Density(_) => sync::handle_remove::<comp::Density>(entity, world),
EcsCompPhantom::Collider(_) => sync::handle_remove::<comp::Collider>(entity, world),
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
EcsCompPhantom::CharacterState(_) => {
sync::handle_remove::<comp::CharacterState>(entity, world)
},
EcsCompPhantom::Pos(_) => sync::handle_interp_remove::<comp::Pos>(entity, world),
EcsCompPhantom::Vel(_) => sync::handle_interp_remove::<comp::Vel>(entity, world),
EcsCompPhantom::Ori(_) => sync::handle_interp_remove::<comp::Ori>(entity, world),
EcsCompPhantom::Shockwave(_) => sync::handle_remove::<comp::Shockwave>(entity, world),
EcsCompPhantom::BeamSegment(_) => {
sync::handle_remove::<comp::BeamSegment>(entity, world)
},
EcsCompPhantom::Alignment(_) => sync::handle_remove::<comp::Alignment>(entity, world),
impl sync::CompPacket for EcsCompPacket {
type Phantom = EcsCompPhantom;
fn apply_insert(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
match self {
$(Self::$component_type(comp) => {
sync::handle_insert(comp, entity, world);
},)*
Self::Pos(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
Self::Vel(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
Self::Ori(comp) => {
sync::handle_interp_insert(comp, entity, world, force_update)
},
}
}
fn apply_modify(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
match self {
$(Self::$component_type(comp) => {
sync::handle_modify(comp, entity, world);
},)*
Self::Pos(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
Self::Vel(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
Self::Ori(comp) => {
sync::handle_interp_modify(comp, entity, world, force_update)
},
}
}
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
match phantom {
$(EcsCompPhantom::$component_type(_) => {
sync::handle_remove::<$component_type>(entity, world);
},)*
EcsCompPhantom::Pos(_) => {
sync::handle_interp_remove::<comp::Pos>(entity, world)
},
EcsCompPhantom::Vel(_) => {
sync::handle_interp_remove::<comp::Vel>(entity, world)
},
EcsCompPhantom::Ori(_) => {
sync::handle_interp_remove::<comp::Ori>(entity, world)
},
}
}
}
}
}
use crate::synced_components::*;
crate::synced_components!(comp_packet);

View File

@ -1,12 +1,14 @@
// Note: Currently only one-way sync is supported until a usecase for two-way
// sync arises
pub mod interpolation;
mod net_sync;
mod packet;
mod sync_ext;
mod track;
// Reexports
pub use common::uid::{Uid, UidAllocator};
pub use net_sync::{NetSync, SyncFrom};
pub use packet::{
handle_insert, handle_interp_insert, handle_interp_modify, handle_interp_remove, handle_modify,
handle_remove, CompPacket, CompSyncPackage, EntityPackage, EntitySyncPackage,

View File

@ -0,0 +1,46 @@
//! Types of syncing:
//! * synced from all entities
//! * synced only from the client's entity
//!
//! Types of updating
//! * Plain copy of the new component state
//! * (unimplemented) Diff to update component, two variants
//! * Keep a full copy of the component and generate diff from that
//! * Intercept changes to the component (no need to compute diff or keep a
//! full copy)
//!
//! NOTE: rapidly updated components like Pos/Vel/Ori are not covered here
/// Trait that must be implemented for most components that are synced over the
/// network.
pub trait NetSync: specs::Component + Clone + Send + Sync
where
Self::Storage: specs::storage::Tracked,
{
// TODO: this scheme theoretically supports diffing withing the
// impl of `From<UpdateFrom> for Update` but there is no automatic
// machinery to provide the `UpdateFrom` value yet. Might need to
// rework this when actuall implementing though.
//
//type UpdateFrom = Self;
//type Update: From<Self::UpdateFrom> = Self;
const SYNC_FROM: SyncFrom;
// sync::handle_modify(comp, entity, world)
/// Allows making modifications before the synced component is inserted on
/// the client.
fn pre_insert(&mut self, world: &specs::World) { let _world = world; }
/// Allows making modifications before the synced component is overwritten
/// with this version on the client.
fn pre_modify(&mut self, world: &specs::World) { let _world = world; }
}
/// Whether a component is synced to the client for all entities or for just the
/// client's own entity.
pub enum SyncFrom {
AllEntities,
ClientEntity,
}

View File

@ -168,15 +168,13 @@ impl<P: CompPacket> CompSyncPackage<P> {
.push((uid.into(), CompUpdateKind::Removed(PhantomData::<C>.into())));
}
#[must_use]
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
mut self,
pub fn add_component_updates<'a, C: Component + Clone + Send + Sync>(
&mut self,
uids: &ReadStorage<'a, Uid>,
tracker: &UpdateTracker<C>,
storage: &ReadStorage<'a, C>,
filter: impl Join + Copy,
) -> Self
where
) where
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
@ -184,6 +182,27 @@ impl<P: CompPacket> CompSyncPackage<P> {
C::Storage: specs::storage::Tracked,
{
tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
self
}
pub fn add_component_update<'a, C: Component + Clone + Send + Sync>(
&mut self,
tracker: &UpdateTracker<C>,
storage: &ReadStorage<'a, C>,
uid: u64,
entity: Entity,
) where
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
P::Phantom: TryInto<PhantomData<C>>,
C::Storage: specs::storage::Tracked,
{
if let Some(comp_update) = tracker.get_update(storage, entity) {
self.comp_updates.push((uid, comp_update))
}
}
/// Returns whether this package is empty, useful for not sending an empty
/// message.
pub fn is_empty(&self) -> bool { self.comp_updates.is_empty() }
}

View File

@ -17,7 +17,7 @@ impl<C: Component> UpdateTracker<C>
where
C::Storage: specs::storage::Tracked,
{
pub fn new(specs_world: &mut World) -> Self {
pub fn new(specs_world: &World) -> Self {
Self {
reader_id: specs_world.write_storage::<C>().register_reader(),
inserted: BitSet::new(),
@ -122,4 +122,38 @@ impl<C: Component + Clone + Send + Sync> UpdateTracker<C> {
));
}
}
/// Returns `Some(update)` if the tracked component was modified for this
/// entity.
pub fn get_update<'a, P>(
&self,
storage: &specs::ReadStorage<'a, C>,
entity: Entity,
) -> Option<CompUpdateKind<P>>
where
P: CompPacket,
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
P::Phantom: TryInto<PhantomData<C>>,
C::Storage: specs::storage::Tracked,
{
let id = entity.id();
// Generate inserted update if one exists
//
// Note: presence of the id in these bitsets should be mutually exclusive
if self.inserted.contains(id) {
storage
.get(entity)
.map(|comp| CompUpdateKind::Inserted(P::from(comp.clone())))
} else if self.modified.contains(id) {
storage
.get(entity)
.map(|comp| CompUpdateKind::Modified(P::from(comp.clone())))
} else if self.removed.contains(id) {
Some(CompUpdateKind::Removed(P::Phantom::from(PhantomData::<C>)))
} else {
None
}
}
}

View File

@ -0,0 +1,211 @@
//! Contains an "x macro" for all synced components as well as [NetSync]
//! implementations for those components.
//!
//!
//! An x macro accepts another macro as input and calls it with a list of
//! inputs. This allows adding components to the list in the x macro declaration
//! and then writing macros that will accept this list and generate code that
//! handles every synced component without further repitition of the component
//! set.
//!
//! This module also re-exports all the component types that are synced.
//!
//! A glob import from this can be used so that the component types are in scope
//! when using the x macro defined here which requires this.
/// This provides a lowercase name and the component type.
//#[rustfmt::skip]
#[macro_export]
macro_rules! synced_components {
($macro:ident) => {
$macro! {
body: Body,
stats: Stats,
buffs: Buffs,
auras: Auras,
energy: Energy,
health: Health,
poise: Poise,
light_emitter: LightEmitter,
item: Item,
scale: Scale,
group: Group,
is_mount: IsMount,
is_rider: IsRider,
mass: Mass,
density: Density,
collider: Collider,
sticky: Sticky,
character_state: CharacterState,
shockwave: Shockwave,
beam_segment: BeamSegment,
alignment: Alignment,
// TODO: evaluate if this can be `FromClient`
combo: Combo,
// TODO: evaluate if this is used on the client,
// and if so what it is used for
player: Player,
// TODO: change this to FromClient and sync the bare minimum
// from other entities (e.g. just keys needed to show appearance
// based on their loadout). Also, it looks like this actually has
// an alternative sync method implemented in entity_sync via
// ServerGeneral::InventoryUpdate so we could use that instead
// or remove the part where it clones the inventory.
inventory: Inventory,
// Synced to the client only for its own entity
skill_set: SkillSet,
active_abilities: ActiveAbilities,
can_build: CanBuild,
}
};
}
macro_rules! reexport_comps {
($($name:ident: $type:ident,)*) => {
mod inner {
pub use common::comp::*;
use common::link::Is;
use common::mounting::{Mount, Rider};
pub type IsMount = Is<Mount>;
pub type IsRider = Is<Rider>;
}
$(pub use inner::$type;)*
}
}
synced_components!(reexport_comps);
// ===============================
// === NetSync implementations ===
// ===============================
use crate::sync::{NetSync, SyncFrom};
impl NetSync for Body {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Stats {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Buffs {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Auras {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Energy {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Health {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
fn pre_insert(&mut self, world: &specs::World) {
use common::resources::Time;
use specs::WorldExt;
// Time isn't synced between client and server so replace the Time from the
// server with the Client's local Time to enable accurate comparison.
self.last_change.time = *world.read_resource::<Time>();
}
fn pre_modify(&mut self, world: &specs::World) {
use common::resources::Time;
use specs::WorldExt;
// Time isn't synced between client and server so replace the Time from the
// server with the Client's local Time to enable accurate comparison.
self.last_change.time = *world.read_resource::<Time>();
}
}
impl NetSync for Poise {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for LightEmitter {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Item {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Scale {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Group {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for IsMount {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for IsRider {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Mass {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Density {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Collider {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Sticky {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for CharacterState {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Shockwave {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for BeamSegment {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Alignment {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Combo {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Player {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for Inventory {
const SYNC_FROM: SyncFrom = SyncFrom::AllEntities;
}
impl NetSync for SkillSet {
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
}
impl NetSync for ActiveAbilities {
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
}
impl NetSync for CanBuild {
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
}

View File

@ -60,7 +60,7 @@ use crate::{
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
rtsim::RtSim,
state_ext::StateExt,
sys::sentinel::{DeletedEntities, TrackedComps},
sys::sentinel::{DeletedEntities, TrackedStorages},
};
#[cfg(not(feature = "worldgen"))]
use common::grid::Grid;
@ -447,7 +447,7 @@ impl Server {
state.ecs_mut().write_resource::<TimeOfDay>().0 = settings.start_time;
// Register trackers
sys::sentinel::register_trackers(state.ecs_mut());
sys::sentinel::UpdateTrackers::register(state.ecs_mut());
state.ecs_mut().insert(DeletedEntities::default());
@ -1025,7 +1025,7 @@ impl Server {
)
.send(ServerInit::GameSync {
// Send client their entity
entity_package: TrackedComps::fetch(self.state.ecs())
entity_package: TrackedStorages::fetch(self.state.ecs())
.create_entity_package(entity, None, None, None)
.expect(
"We just created this entity as marked() (using create_entity_synced) so \

View File

@ -1,4 +1,4 @@
use super::sentinel::{DeletedEntities, ReadTrackers, TrackedComps};
use super::sentinel::{DeletedEntities, TrackedStorages, UpdateTrackers};
use crate::{
client::Client,
presence::{Presence, RegionSubscription},
@ -6,9 +6,7 @@ use crate::{
};
use common::{
calendar::Calendar,
comp::{Collider, ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
link::Is,
mounting::Rider,
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Pos, Vel},
outcome::Outcome,
region::{Event as RegionEvent, RegionMap},
resources::{PlayerPhysicsSettings, TimeOfDay},
@ -31,30 +29,25 @@ impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, Tick>,
Read<'a, PlayerPhysicsSettings>,
TrackedStorages<'a>,
ReadExpect<'a, TimeOfDay>,
ReadExpect<'a, Calendar>,
ReadExpect<'a, RegionMap>,
ReadStorage<'a, Uid>,
ReadExpect<'a, UpdateTrackers>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Vel>,
ReadStorage<'a, Ori>,
ReadStorage<'a, Inventory>,
ReadStorage<'a, RegionSubscription>,
ReadStorage<'a, Presence>,
ReadStorage<'a, Collider>,
ReadStorage<'a, Client>,
WriteStorage<'a, Last<Pos>>,
WriteStorage<'a, Last<Vel>>,
WriteStorage<'a, Last<Ori>>,
ReadStorage<'a, Client>,
WriteStorage<'a, ForceUpdate>,
WriteStorage<'a, InventoryUpdate>,
Write<'a, DeletedEntities>,
Write<'a, Vec<Outcome>>,
Read<'a, PlayerPhysicsSettings>,
ReadStorage<'a, Is<Rider>>,
ReadStorage<'a, Player>,
TrackedComps<'a>,
ReadTrackers<'a>,
);
const NAME: &'static str = "entity_sync";
@ -66,33 +59,35 @@ impl<'a> System<'a> for Sys {
(
entities,
tick,
player_physics_settings,
tracked_storages,
time_of_day,
calendar,
region_map,
uids,
trackers,
positions,
velocities,
orientations,
inventories,
subscriptions,
presences,
colliders,
clients,
mut last_pos,
mut last_vel,
mut last_ori,
clients,
mut force_updates,
mut inventory_updates,
mut deleted_entities,
mut outcomes,
player_physics_settings,
is_rider,
players,
tracked_comps,
trackers,
): Self::SystemData,
) {
let tick = tick.0;
let uids = &tracked_storages.uid;
let colliders = &tracked_storages.collider;
let inventories = &tracked_storages.inventory;
let players = &tracked_storages.player;
let is_rider = &tracked_storages.is_rider;
// To send entity updates
// 1. Iterate through regions
// 2. Iterate through region subscribers (ie clients)
@ -161,7 +156,7 @@ impl<'a> System<'a> for Sys {
.get(entity)
.map(|pos| (pos, velocities.get(entity), orientations.get(entity)))
.and_then(|(pos, vel, ori)| {
tracked_comps.create_entity_package(
tracked_storages.create_entity_package(
entity,
Some(*pos),
vel.copied(),
@ -203,7 +198,7 @@ impl<'a> System<'a> for Sys {
// Sync tracked components
// Get deleted entities in this region from DeletedEntities
let (entity_sync_package, comp_sync_package) = trackers.create_sync_packages(
&tracked_comps,
&tracked_storages,
region.entities(),
deleted_entities_in_region,
);
@ -232,7 +227,7 @@ impl<'a> System<'a> for Sys {
for (_, entity, &uid, (&pos, last_pos), vel, ori, force_update, collider) in (
region.entities(),
&entities,
&uids,
uids,
(&positions, last_pos.mask().maybe()),
(&velocities, last_vel.mask().maybe()).maybe(),
(&orientations, last_vel.mask().maybe()).maybe(),
@ -353,13 +348,22 @@ impl<'a> System<'a> for Sys {
// TODO: Sync clients that don't have a position?
// Sync inventories
for (inventory, update, client) in (&inventories, &inventory_updates, &clients).join() {
for (inventory, update, client) in (inventories, &inventory_updates, &clients).join() {
client.send_fallible(ServerGeneral::InventoryUpdate(
inventory.clone(),
update.event(),
));
}
// Sync components that just sync to the client's entity
for (entity, client) in (&entities, &clients).join() {
let comp_sync_package =
trackers.create_sync_from_client_package(&tracked_storages, entity);
if !comp_sync_package.is_empty() {
client.send_fallible(ServerGeneral::CompSync(comp_sync_package));
}
}
// Sync outcomes
for (presence, pos, client) in (presences.maybe(), positions.maybe(), &clients).join() {
let is_near = |o_pos: Vec3<f32>| {

View File

@ -2,18 +2,14 @@
use common::{
comp::{
item::{tool::AbilityMap, MaterialStatManifest},
ActiveAbilities, Alignment, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState,
Collider, Combo, Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass, Ori,
Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
Ori, Pos, Vel,
},
link::Is,
mounting::{Mount, Rider},
uid::Uid,
};
use common_ecs::{Job, Origin, Phase, System};
use common_net::{
msg::EcsCompPacket,
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, UpdateTracker, WorldSyncExt},
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, NetSync, SyncFrom, UpdateTracker},
};
use hashbrown::HashMap;
use specs::{
@ -28,400 +24,183 @@ use vek::*;
#[derive(Default)]
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (TrackedComps<'a>, WriteTrackers<'a>);
type SystemData = (TrackedStorages<'a>, WriteExpect<'a, UpdateTrackers>);
const NAME: &'static str = "sentinel";
const ORIGIN: Origin = Origin::Server;
const PHASE: Phase = Phase::Create;
fn run(_job: &mut Job<Self>, (comps, mut trackers): Self::SystemData) {
record_changes(&comps, &mut trackers);
fn run(_job: &mut Job<Self>, (storages, mut trackers): Self::SystemData) {
trackers.record_changes(&storages);
}
}
// Probably more difficult than it needs to be :p
#[derive(SystemData)]
pub struct TrackedComps<'a> {
pub uid: ReadStorage<'a, Uid>,
pub body: ReadStorage<'a, Body>,
pub player: ReadStorage<'a, Player>,
pub stats: ReadStorage<'a, Stats>,
pub skill_set: ReadStorage<'a, SkillSet>,
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
pub buffs: ReadStorage<'a, Buffs>,
pub auras: ReadStorage<'a, Auras>,
pub energy: ReadStorage<'a, Energy>,
pub combo: ReadStorage<'a, Combo>,
pub health: ReadStorage<'a, Health>,
pub poise: ReadStorage<'a, Poise>,
pub can_build: ReadStorage<'a, CanBuild>,
pub light_emitter: ReadStorage<'a, LightEmitter>,
pub item: ReadStorage<'a, Item>,
pub scale: ReadStorage<'a, Scale>,
pub is_mount: ReadStorage<'a, Is<Mount>>,
pub is_rider: ReadStorage<'a, Is<Rider>>,
pub group: ReadStorage<'a, Group>,
pub mass: ReadStorage<'a, Mass>,
pub density: ReadStorage<'a, Density>,
pub collider: ReadStorage<'a, Collider>,
pub sticky: ReadStorage<'a, Sticky>,
pub inventory: ReadStorage<'a, Inventory>,
pub character_state: ReadStorage<'a, CharacterState>,
pub shockwave: ReadStorage<'a, Shockwave>,
pub beam_segment: ReadStorage<'a, BeamSegment>,
pub alignment: ReadStorage<'a, Alignment>,
/// Holds state like modified bitsets, modification event readers
macro_rules! trackers {
($($component_name:ident: $component_type:ident,)*) => {
#[derive(SystemData)]
pub struct TrackedStorages<'a> {
pub uid: ReadStorage<'a, Uid>,
$(pub $component_name: ReadStorage<'a, $component_type>,)*
pub _ability_map: ReadExpect<'a, AbilityMap>,
pub _msm: ReadExpect<'a, MaterialStatManifest>,
}
pub ability_map: ReadExpect<'a, AbilityMap>,
pub msm: ReadExpect<'a, MaterialStatManifest>,
}
impl<'a> TrackedComps<'a> {
pub fn create_entity_package(
&self,
entity: EcsEntity,
pos: Option<Pos>,
vel: Option<Vel>,
ori: Option<Ori>,
) -> Option<EntityPackage<EcsCompPacket>> {
let uid = self.uid.get(entity).copied()?.0;
let mut comps = Vec::new();
self.body.get(entity).copied().map(|c| comps.push(c.into()));
self.player
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.stats
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.skill_set
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.active_abilities
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.buffs
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.auras
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.energy
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.combo
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.health
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.poise
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.can_build
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.light_emitter
.get(entity)
.copied()
.map(|c| comps.push(c.into()));
self.item
.get(entity)
.map(|item| item.duplicate(&self.ability_map, &self.msm))
.map(|c| comps.push(c.into()));
self.scale
.get(entity)
.copied()
.map(|c| comps.push(c.into()));
self.is_mount
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.is_rider
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.group
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
self.density
.get(entity)
.copied()
.map(|c| comps.push(c.into()));
self.collider
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.sticky
.get(entity)
.copied()
.map(|c| comps.push(c.into()));
self.inventory
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.character_state
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.shockwave
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.beam_segment
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.alignment
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
// Add untracked comps
pos.map(|c| comps.push(c.into()));
vel.map(|c| comps.push(c.into()));
ori.map(|c| comps.push(c.into()));
impl TrackedStorages<'_> {
pub fn create_entity_package(
&self,
entity: EcsEntity,
pos: Option<Pos>,
vel: Option<Vel>,
ori: Option<Ori>,
) -> Option<EntityPackage<EcsCompPacket>> {
let uid = self.uid.get(entity).copied()?.0;
let mut comps = Vec::new();
// NOTE: we could potentially include a bitmap indicating which components are present instead of tagging
// components with the type in order to save bandwidth
//
// if the number of optional components sent is less than 1/8 of the number of component types then
// then the suggested approach would no longer be favorable
$(
Some(EntityPackage { uid, comps })
}
}
#[derive(SystemData)]
pub struct ReadTrackers<'a> {
pub uid: ReadExpect<'a, UpdateTracker<Uid>>,
pub body: ReadExpect<'a, UpdateTracker<Body>>,
pub player: ReadExpect<'a, UpdateTracker<Player>>,
pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
pub skill_set: ReadExpect<'a, UpdateTracker<SkillSet>>,
pub active_abilities: ReadExpect<'a, UpdateTracker<ActiveAbilities>>,
pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>,
pub auras: ReadExpect<'a, UpdateTracker<Auras>>,
pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
pub combo: ReadExpect<'a, UpdateTracker<Combo>>,
pub health: ReadExpect<'a, UpdateTracker<Health>>,
pub poise: ReadExpect<'a, UpdateTracker<Poise>>,
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
pub light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
pub inventory: ReadExpect<'a, UpdateTracker<Inventory>>,
pub item: ReadExpect<'a, UpdateTracker<Item>>,
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
pub is_mount: ReadExpect<'a, UpdateTracker<Is<Mount>>>,
pub is_rider: ReadExpect<'a, UpdateTracker<Is<Rider>>>,
pub group: ReadExpect<'a, UpdateTracker<Group>>,
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
pub density: ReadExpect<'a, UpdateTracker<Density>>,
pub collider: ReadExpect<'a, UpdateTracker<Collider>>,
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
pub shockwave: ReadExpect<'a, UpdateTracker<Shockwave>>,
pub beam_segment: ReadExpect<'a, UpdateTracker<BeamSegment>>,
pub alignment: ReadExpect<'a, UpdateTracker<Alignment>>,
}
impl<'a> ReadTrackers<'a> {
pub fn create_sync_packages(
&self,
comps: &TrackedComps,
filter: impl Join + Copy,
deleted_entities: Vec<u64>,
) -> (EntitySyncPackage, CompSyncPackage<EcsCompPacket>) {
let entity_sync_package =
EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities);
let comp_sync_package = CompSyncPackage::new()
.with_component(&comps.uid, &*self.body, &comps.body, filter)
.with_component(&comps.uid, &*self.player, &comps.player, filter)
.with_component(&comps.uid, &*self.stats, &comps.stats, filter)
.with_component(&comps.uid, &*self.skill_set, &comps.skill_set, filter)
.with_component(
&comps.uid,
&*self.active_abilities,
&comps.active_abilities,
filter,
)
.with_component(&comps.uid, &*self.buffs, &comps.buffs, filter)
.with_component(&comps.uid, &*self.auras, &comps.auras, filter)
.with_component(&comps.uid, &*self.energy, &comps.energy, filter)
.with_component(&comps.uid, &*self.combo, &comps.combo, filter)
.with_component(&comps.uid, &*self.health, &comps.health, filter)
.with_component(&comps.uid, &*self.poise, &comps.poise, filter)
.with_component(&comps.uid, &*self.can_build, &comps.can_build, filter)
.with_component(
&comps.uid,
&*self.light_emitter,
&comps.light_emitter,
filter,
)
.with_component(&comps.uid, &*self.item, &comps.item, filter)
.with_component(&comps.uid, &*self.scale, &comps.scale, filter)
.with_component(&comps.uid, &*self.is_mount, &comps.is_mount, filter)
.with_component(&comps.uid, &*self.is_rider, &comps.is_rider, filter)
.with_component(&comps.uid, &*self.group, &comps.group, filter)
.with_component(&comps.uid, &*self.mass, &comps.mass, filter)
.with_component(&comps.uid, &*self.density, &comps.density, filter)
.with_component(&comps.uid, &*self.collider, &comps.collider, filter)
.with_component(&comps.uid, &*self.sticky, &comps.sticky, filter)
.with_component(&comps.uid, &*self.inventory, &comps.inventory, filter)
.with_component(
&comps.uid,
&*self.character_state,
&comps.character_state,
filter,
)
.with_component(&comps.uid, &*self.shockwave, &comps.shockwave, filter)
.with_component(&comps.uid, &*self.beam_segment, &comps.beam_segment, filter)
.with_component(&comps.uid, &*self.alignment, &comps.alignment, filter);
if matches!(
<$component_type as NetSync>::SYNC_FROM,
SyncFrom::AllEntities,
) {
self
.$component_name
.get(entity)
// TODO: should duplicate rather than clone the item
//
// NetClone trait?
//
//.map(|item| item.duplicate(&self.ability_map, &self.msm))
.cloned()
.map(|c| comps.push(c.into()));
}
)*
// Add untracked comps
pos.map(|c| comps.push(c.into()));
vel.map(|c| comps.push(c.into()));
ori.map(|c| comps.push(c.into()));
(entity_sync_package, comp_sync_package)
Some(EntityPackage { uid, comps })
}
}
/// This should be inserted into the ecs as a Resource
pub struct UpdateTrackers {
pub uid: UpdateTracker<Uid>,
$($component_name: UpdateTracker<$component_type>,)*
}
impl UpdateTrackers {
/// Constructs the update trackers and inserts it into the world as a resource.
///
/// Components that will be synced must already be registered.
pub fn register(world: &mut specs::World) {
let trackers = UpdateTrackers {
uid: UpdateTracker::<Uid>::new(&world),
$($component_name: UpdateTracker::<$component_type>::new(&world),)*
};
world.insert(trackers);
// TODO: if we held copies of components for doing diffing, the components that hold that data could be registered here
}
/// Update trackers
fn record_changes(&mut self, comps: &TrackedStorages) {
self.uid.record_changes(&comps.uid);
$(
self.$component_name.record_changes(&comps.$component_name);
)*
// TODO: integrate env var switches to report counts to tracy/etc
// Debug how many updates are being sent
/*
macro_rules! log_counts {
($comp:ident, $name:expr) => {
// Note: if this will be used in actual server it would be more efficient to
// count during record_changes
let tracker = &self.$comp;
let inserted = tracker.inserted().into_iter().count();
let modified = tracker.modified().into_iter().count();
let removed = tracker.removed().into_iter().count();
tracing::warn!("{:6} insertions detected for {}", inserted, $name);
tracing::warn!("{:6} modifications detected for {}", modified, $name);
tracing::warn!("{:6} deletions detected for {}", removed, $name);
};
};
$(log_counts!($component_name, concat!(stringify!($component_name), 's'));)* */
}
pub fn create_sync_packages(
&self,
comps: &TrackedStorages,
filter: impl Join + Copy,
deleted_entities: Vec<u64>,
) -> (EntitySyncPackage, CompSyncPackage<EcsCompPacket>) {
let entity_sync_package =
EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities);
let mut comp_sync_package = CompSyncPackage::new();
$(
if matches!(
<$component_type as NetSync>::SYNC_FROM,
SyncFrom::AllEntities,
) {
comp_sync_package.add_component_updates(
&comps.uid,
&self.$component_name,
&comps.$component_name,
filter,
);
}
)*
(entity_sync_package, comp_sync_package)
}
/// Create sync package for components that are only synced to the client entity
pub fn create_sync_from_client_package(
&self,
comps: &TrackedStorages,
entity: specs::Entity,
) -> CompSyncPackage<EcsCompPacket> {
// TODO: this type repeats the entity uid for each component but
// we know they will all be the same here, using it for now for
// convenience but it could help to make a specific type for this
// later.
let mut comp_sync_package = CompSyncPackage::new();
let uid = match comps.uid.get(entity) {
Some(uid) => (*uid).into(),
// Return empty package if we can't get uid for this entity
None => return comp_sync_package,
};
$(
if matches!(
<$component_type as NetSync>::SYNC_FROM,
SyncFrom::ClientEntity,
) {
comp_sync_package.add_component_update(
&self.$component_name,
&comps.$component_name,
uid,
entity,
);
}
)*
comp_sync_package
}
}
}
}
#[derive(SystemData)]
pub struct WriteTrackers<'a> {
uid: WriteExpect<'a, UpdateTracker<Uid>>,
body: WriteExpect<'a, UpdateTracker<Body>>,
player: WriteExpect<'a, UpdateTracker<Player>>,
stats: WriteExpect<'a, UpdateTracker<Stats>>,
skill_set: WriteExpect<'a, UpdateTracker<SkillSet>>,
active_abilities: WriteExpect<'a, UpdateTracker<ActiveAbilities>>,
buffs: WriteExpect<'a, UpdateTracker<Buffs>>,
auras: WriteExpect<'a, UpdateTracker<Auras>>,
energy: WriteExpect<'a, UpdateTracker<Energy>>,
combo: WriteExpect<'a, UpdateTracker<Combo>>,
health: WriteExpect<'a, UpdateTracker<Health>>,
poise: WriteExpect<'a, UpdateTracker<Poise>>,
can_build: WriteExpect<'a, UpdateTracker<CanBuild>>,
light_emitter: WriteExpect<'a, UpdateTracker<LightEmitter>>,
item: WriteExpect<'a, UpdateTracker<Item>>,
scale: WriteExpect<'a, UpdateTracker<Scale>>,
is_mounts: WriteExpect<'a, UpdateTracker<Is<Mount>>>,
is_riders: WriteExpect<'a, UpdateTracker<Is<Rider>>>,
group: WriteExpect<'a, UpdateTracker<Group>>,
mass: WriteExpect<'a, UpdateTracker<Mass>>,
density: WriteExpect<'a, UpdateTracker<Density>>,
collider: WriteExpect<'a, UpdateTracker<Collider>>,
sticky: WriteExpect<'a, UpdateTracker<Sticky>>,
inventory: WriteExpect<'a, UpdateTracker<Inventory>>,
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
shockwave: WriteExpect<'a, UpdateTracker<Shockwave>>,
beam: WriteExpect<'a, UpdateTracker<BeamSegment>>,
alignment: WriteExpect<'a, UpdateTracker<Alignment>>,
}
fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
// Update trackers
trackers.uid.record_changes(&comps.uid);
trackers.body.record_changes(&comps.body);
trackers.player.record_changes(&comps.player);
trackers.stats.record_changes(&comps.stats);
trackers.skill_set.record_changes(&comps.skill_set);
trackers
.active_abilities
.record_changes(&comps.active_abilities);
trackers.buffs.record_changes(&comps.buffs);
trackers.auras.record_changes(&comps.auras);
trackers.energy.record_changes(&comps.energy);
trackers.combo.record_changes(&comps.combo);
trackers.health.record_changes(&comps.health);
trackers.poise.record_changes(&comps.poise);
trackers.can_build.record_changes(&comps.can_build);
trackers.light_emitter.record_changes(&comps.light_emitter);
trackers.item.record_changes(&comps.item);
trackers.scale.record_changes(&comps.scale);
trackers.is_mounts.record_changes(&comps.is_mount);
trackers.is_riders.record_changes(&comps.is_rider);
trackers.group.record_changes(&comps.group);
trackers.mass.record_changes(&comps.mass);
trackers.density.record_changes(&comps.density);
trackers.collider.record_changes(&comps.collider);
trackers.sticky.record_changes(&comps.sticky);
trackers.inventory.record_changes(&comps.inventory);
trackers
.character_state
.record_changes(&comps.character_state);
trackers.shockwave.record_changes(&comps.shockwave);
trackers.beam.record_changes(&comps.beam_segment);
trackers.alignment.record_changes(&comps.alignment);
// Debug how many updates are being sent
/*
macro_rules! log_counts {
($comp:ident, $name:expr) => {
// Note: if this will be used in actual server it would be more efficient to
// count during record_changes
let tracker = &trackers.$comp;
let inserted = tracker.inserted().into_iter().count();
let modified = tracker.modified().into_iter().count();
let removed = tracker.removed().into_iter().count();
tracing::warn!("{:6} insertions detected for {}", inserted, $name);
tracing::warn!("{:6} modifications detected for {}", modified, $name);
tracing::warn!("{:6} deletions detected for {}", removed, $name);
};
};
log_counts!(uid, "Uids");
log_counts!(body, "Bodies");
log_counts!(buffs, "Buffs");
log_counts!(auras, "Auras");
log_counts!(player, "Players");
log_counts!(stats, "Stats");
log_counts!(skill_set, "SkillSet");
log_counts!(active_abilities, "ActiveAbilities");
log_counts!(energy, "Energies");
log_counts!(combo, "Combos");
log_vounts!(health, "Healths");
log_vounts!(poise, "Poises");
log_counts!(light_emitter, "Light emitters");
log_counts!(item, "Items");
log_counts!(scale, "Scales");
log_counts!(is_mounts, "mounts");
log_counts!(is_riders, "riders");
log_counts!(mass, "Masses");
log_counts!(mass, "Densities");
log_counts!(collider, "Colliders");
log_counts!(sticky, "Stickies");
log_counts!(loadout, "Loadouts");
log_counts!(character_state, "Character States");
log_counts!(shockwave, "Shockwaves");
log_counts!(beam, "Beams");
log_counts!(alignment, "Alignments");
*/
}
pub fn register_trackers(world: &mut World) {
world.register_tracker::<Uid>();
world.register_tracker::<Body>();
world.register_tracker::<Player>();
world.register_tracker::<Stats>();
world.register_tracker::<SkillSet>();
world.register_tracker::<ActiveAbilities>();
world.register_tracker::<Buffs>();
world.register_tracker::<Auras>();
world.register_tracker::<Energy>();
world.register_tracker::<Combo>();
world.register_tracker::<Health>();
world.register_tracker::<Poise>();
world.register_tracker::<CanBuild>();
world.register_tracker::<LightEmitter>();
world.register_tracker::<Item>();
world.register_tracker::<Scale>();
world.register_tracker::<Is<Mount>>();
world.register_tracker::<Is<Rider>>();
world.register_tracker::<Group>();
world.register_tracker::<Mass>();
world.register_tracker::<Density>();
world.register_tracker::<Collider>();
world.register_tracker::<Sticky>();
world.register_tracker::<Inventory>();
world.register_tracker::<CharacterState>();
world.register_tracker::<Shockwave>();
world.register_tracker::<BeamSegment>();
world.register_tracker::<Alignment>();
}
use common_net::synced_components::*;
common_net::synced_components!(trackers);
/// Deleted entities grouped by region
pub struct DeletedEntities {

View File

@ -1,4 +1,4 @@
use super::sentinel::{DeletedEntities, TrackedComps};
use super::sentinel::{DeletedEntities, TrackedStorages};
use crate::{
client::Client,
presence::{self, Presence, RegionSubscription},
@ -34,7 +34,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Client>,
WriteStorage<'a, RegionSubscription>,
Read<'a, DeletedEntities>,
TrackedComps<'a>,
TrackedStorages<'a>,
);
const NAME: &'static str = "subscription";
@ -222,7 +222,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
);
let region_map = world.read_resource::<RegionMap>();
let tracked_comps = TrackedComps::fetch(world);
let tracked_comps = TrackedStorages::fetch(world);
for key in &regions {
if let Some(region) = region_map.get(*key) {
(