mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
c4706078c4
commit
aa122c1a18
@ -2,3 +2,4 @@
|
||||
#![feature(generic_const_exprs, const_fn_floating_point_arithmetic)]
|
||||
pub mod msg;
|
||||
pub mod sync;
|
||||
pub mod synced_components;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
46
common/net/src/sync/net_sync.rs
Normal file
46
common/net/src/sync/net_sync.rs
Normal 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,
|
||||
}
|
@ -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() }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
211
common/net/src/synced_components.rs
Normal file
211
common/net/src/synced_components.rs
Normal 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;
|
||||
}
|
@ -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 \
|
||||
|
@ -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>| {
|
||||
|
@ -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 {
|
||||
|
@ -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 ®ions {
|
||||
if let Some(region) = region_map.get(*key) {
|
||||
(
|
||||
|
Loading…
Reference in New Issue
Block a user