mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/component-sync' into 'master'
Sync some components only from the client's own entity. See merge request veloren/veloren!3108
This commit is contained in:
@ -6,6 +6,10 @@ pub use userdata_dir::userdata_dir;
|
|||||||
|
|
||||||
#[cfg(feature = "tracy")] pub use tracy_client;
|
#[cfg(feature = "tracy")] pub use tracy_client;
|
||||||
|
|
||||||
|
/// Allows downstream crates to conditionally do things based on whether tracy
|
||||||
|
/// is enabled without having to expose a cargo feature themselves.
|
||||||
|
pub const TRACY_ENABLED: bool = cfg!(feature = "tracy");
|
||||||
|
|
||||||
#[cfg(not(feature = "tracy"))]
|
#[cfg(not(feature = "tracy"))]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! plot {
|
macro_rules! plot {
|
||||||
|
@ -2,3 +2,4 @@
|
|||||||
#![feature(generic_const_exprs, const_fn_floating_point_arithmetic)]
|
#![feature(generic_const_exprs, const_fn_floating_point_arithmetic)]
|
||||||
pub mod msg;
|
pub mod msg;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
pub mod synced_components;
|
||||||
|
@ -1,224 +1,106 @@
|
|||||||
use crate::sync;
|
use crate::sync::{self, NetSync};
|
||||||
use common::{
|
use common::comp;
|
||||||
comp,
|
|
||||||
link::Is,
|
|
||||||
mounting::{Mount, Rider},
|
|
||||||
resources::Time,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::WorldExt;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use sum_type::sum_type;
|
|
||||||
|
|
||||||
// Automatically derive From<T> for EcsCompPacket
|
/// This macro defines [`EcsCompPacke`]
|
||||||
// for each variant EcsCompPacket::T(T.)
|
///
|
||||||
sum_type! {
|
/// It is meant to be passed to the `synced_components!` macro which will call
|
||||||
|
/// it with a list of components.
|
||||||
|
macro_rules! comp_packet {
|
||||||
|
($($component_name:ident: $component_type:ident,)*) => {
|
||||||
|
|
||||||
|
// `sum_type!` will automatically derive From<T> for EcsCompPacket
|
||||||
|
// for each variant EcsCompPacket::T(T).
|
||||||
|
sum_type::sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPacket {
|
pub enum EcsCompPacket {
|
||||||
Body(comp::Body),
|
// Note: also use the component_type identifier
|
||||||
Player(comp::Player),
|
// to name the enum variant that contains the component.
|
||||||
CanBuild(comp::CanBuild),
|
$($component_type($component_type),)*
|
||||||
Stats(comp::Stats),
|
// These aren't included in the "synced_components" because the way
|
||||||
SkillSet(comp::SkillSet),
|
// we determine if they are changed and when to send them is different
|
||||||
ActiveAbilities(comp::ActiveAbilities),
|
// from the other components.
|
||||||
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),
|
Pos(comp::Pos),
|
||||||
Vel(comp::Vel),
|
Vel(comp::Vel),
|
||||||
Ori(comp::Ori),
|
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!` will automatically derive From<PhantomData<T>> for EcsCompPhantom
|
||||||
sum_type! {
|
// for each variant EcsCompPhantom::T(PhantomData<T>).
|
||||||
|
sum_type::sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPhantom {
|
pub enum EcsCompPhantom {
|
||||||
Body(PhantomData<comp::Body>),
|
$($component_type(PhantomData<$component_type>),)*
|
||||||
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>),
|
Pos(PhantomData<comp::Pos>),
|
||||||
Vel(PhantomData<comp::Vel>),
|
Vel(PhantomData<comp::Vel>),
|
||||||
Ori(PhantomData<comp::Ori>),
|
Ori(PhantomData<comp::Ori>),
|
||||||
Shockwave(PhantomData<comp::Shockwave>),
|
|
||||||
BeamSegment(PhantomData<comp::BeamSegment>),
|
|
||||||
Alignment(PhantomData<comp::Alignment>),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl sync::CompPacket for EcsCompPacket {
|
|
||||||
|
impl sync::CompPacket for EcsCompPacket {
|
||||||
type Phantom = EcsCompPhantom;
|
type Phantom = EcsCompPhantom;
|
||||||
|
|
||||||
fn apply_insert(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
|
fn apply_insert(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
|
||||||
match self {
|
match self {
|
||||||
EcsCompPacket::Body(comp) => sync::handle_insert(comp, entity, world),
|
$(Self::$component_type(mut comp) => {
|
||||||
EcsCompPacket::Player(comp) => sync::handle_insert(comp, entity, world),
|
comp.pre_insert(world);
|
||||||
EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world),
|
sync::handle_insert(comp, entity, world);
|
||||||
EcsCompPacket::Stats(comp) => sync::handle_insert(comp, entity, world),
|
},)*
|
||||||
EcsCompPacket::SkillSet(comp) => sync::handle_insert(comp, entity, world),
|
Self::Pos(comp) => {
|
||||||
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)
|
sync::handle_interp_insert(comp, entity, world, force_update)
|
||||||
},
|
},
|
||||||
EcsCompPacket::Vel(comp) => {
|
Self::Vel(comp) => {
|
||||||
sync::handle_interp_insert(comp, entity, world, force_update)
|
sync::handle_interp_insert(comp, entity, world, force_update)
|
||||||
},
|
},
|
||||||
EcsCompPacket::Ori(comp) => {
|
Self::Ori(comp) => {
|
||||||
sync::handle_interp_insert(comp, entity, world, force_update)
|
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_modify(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
|
fn apply_modify(self, entity: specs::Entity, world: &specs::World, force_update: bool) {
|
||||||
match self {
|
match self {
|
||||||
EcsCompPacket::Body(comp) => sync::handle_modify(comp, entity, world),
|
$(Self::$component_type(mut comp) => {
|
||||||
EcsCompPacket::Player(comp) => sync::handle_modify(comp, entity, world),
|
comp.pre_modify(world);
|
||||||
EcsCompPacket::CanBuild(comp) => sync::handle_modify(comp, entity, world),
|
sync::handle_modify(comp, entity, world);
|
||||||
EcsCompPacket::Stats(comp) => sync::handle_modify(comp, entity, world),
|
},)*
|
||||||
EcsCompPacket::SkillSet(comp) => sync::handle_modify(comp, entity, world),
|
Self::Pos(comp) => {
|
||||||
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)
|
sync::handle_interp_modify(comp, entity, world, force_update)
|
||||||
},
|
},
|
||||||
EcsCompPacket::Vel(comp) => {
|
Self::Vel(comp) => {
|
||||||
sync::handle_interp_modify(comp, entity, world, force_update)
|
sync::handle_interp_modify(comp, entity, world, force_update)
|
||||||
},
|
},
|
||||||
EcsCompPacket::Ori(comp) => {
|
Self::Ori(comp) => {
|
||||||
sync::handle_interp_modify(comp, entity, world, force_update)
|
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
|
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
|
||||||
match phantom {
|
match phantom {
|
||||||
EcsCompPhantom::Body(_) => sync::handle_remove::<comp::Body>(entity, world),
|
$(EcsCompPhantom::$component_type(_) => {
|
||||||
EcsCompPhantom::Player(_) => sync::handle_remove::<comp::Player>(entity, world),
|
sync::handle_remove::<$component_type>(entity, world);
|
||||||
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
|
},)*
|
||||||
EcsCompPhantom::Stats(_) => sync::handle_remove::<comp::Stats>(entity, world),
|
EcsCompPhantom::Pos(_) => {
|
||||||
EcsCompPhantom::SkillSet(_) => sync::handle_remove::<comp::SkillSet>(entity, world),
|
sync::handle_interp_remove::<comp::Pos>(entity, world)
|
||||||
EcsCompPhantom::ActiveAbilities(_) => {
|
|
||||||
sync::handle_remove::<comp::ActiveAbilities>(entity, world)
|
|
||||||
},
|
},
|
||||||
EcsCompPhantom::Buffs(_) => sync::handle_remove::<comp::Buffs>(entity, world),
|
EcsCompPhantom::Vel(_) => {
|
||||||
EcsCompPhantom::Auras(_) => sync::handle_remove::<comp::Auras>(entity, world),
|
sync::handle_interp_remove::<comp::Vel>(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::Ori(_) => {
|
||||||
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
|
sync::handle_interp_remove::<comp::Ori>(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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import all the component types so they will be available when expanding the
|
||||||
|
// macro below.
|
||||||
|
use crate::synced_components::*;
|
||||||
|
// Pass `comp_packet!` macro to this "x macro" which will invoke it with a list
|
||||||
|
// of components. This will declare the types defined in the macro above.
|
||||||
|
crate::synced_components!(comp_packet);
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// Note: Currently only one-way sync is supported until a usecase for two-way
|
// Note: Currently only one-way sync is supported until a usecase for two-way
|
||||||
// sync arises
|
// sync arises
|
||||||
pub mod interpolation;
|
pub mod interpolation;
|
||||||
|
mod net_sync;
|
||||||
mod packet;
|
mod packet;
|
||||||
mod sync_ext;
|
mod sync_ext;
|
||||||
mod track;
|
mod track;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use common::uid::{Uid, UidAllocator};
|
pub use common::uid::{Uid, UidAllocator};
|
||||||
|
pub use net_sync::{NetSync, SyncFrom};
|
||||||
pub use packet::{
|
pub use packet::{
|
||||||
handle_insert, handle_interp_insert, handle_interp_modify, handle_interp_remove, handle_modify,
|
handle_insert, handle_interp_insert, handle_interp_modify, handle_interp_remove, handle_modify,
|
||||||
handle_remove, CompPacket, CompSyncPackage, EntityPackage, EntitySyncPackage,
|
handle_remove, CompPacket, CompSyncPackage, EntityPackage, EntitySyncPackage,
|
||||||
|
50
common/net/src/sync/net_sync.rs
Normal file
50
common/net/src/sync/net_sync.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//! Types of syncing:
|
||||||
|
//! * synced from any entity (within range)
|
||||||
|
//! * 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;
|
||||||
|
|
||||||
|
/// Determines what for entities this component is synced to the client.
|
||||||
|
///
|
||||||
|
/// For example, [`SyncFrom::ClientEntity`] can be used to only sync the
|
||||||
|
/// components for the client's own entity.
|
||||||
|
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 any entity or for just the
|
||||||
|
/// client's own entity.
|
||||||
|
pub enum SyncFrom {
|
||||||
|
AnyEntity,
|
||||||
|
ClientEntity,
|
||||||
|
}
|
@ -168,15 +168,13 @@ impl<P: CompPacket> CompSyncPackage<P> {
|
|||||||
.push((uid.into(), CompUpdateKind::Removed(PhantomData::<C>.into())));
|
.push((uid.into(), CompUpdateKind::Removed(PhantomData::<C>.into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn add_component_updates<'a, C: Component + Clone + Send + Sync>(
|
||||||
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
|
&mut self,
|
||||||
mut self,
|
|
||||||
uids: &ReadStorage<'a, Uid>,
|
uids: &ReadStorage<'a, Uid>,
|
||||||
tracker: &UpdateTracker<C>,
|
tracker: &UpdateTracker<C>,
|
||||||
storage: &ReadStorage<'a, C>,
|
storage: &ReadStorage<'a, C>,
|
||||||
filter: impl Join + Copy,
|
filter: impl Join + Copy,
|
||||||
) -> Self
|
) where
|
||||||
where
|
|
||||||
P: From<C>,
|
P: From<C>,
|
||||||
C: TryFrom<P>,
|
C: TryFrom<P>,
|
||||||
P::Phantom: From<PhantomData<C>>,
|
P::Phantom: From<PhantomData<C>>,
|
||||||
@ -184,6 +182,29 @@ impl<P: CompPacket> CompSyncPackage<P> {
|
|||||||
C::Storage: specs::storage::Tracked,
|
C::Storage: specs::storage::Tracked,
|
||||||
{
|
{
|
||||||
tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
|
tracker.get_updates_for(uids, storage, filter, &mut self.comp_updates);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If there was an update to the component `C` on the provided entity this
|
||||||
|
/// will add the update to this package.
|
||||||
|
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
|
where
|
||||||
C::Storage: specs::storage::Tracked,
|
C::Storage: specs::storage::Tracked,
|
||||||
{
|
{
|
||||||
pub fn new(specs_world: &mut World) -> Self {
|
pub fn new(specs_world: &World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reader_id: specs_world.write_storage::<C>().register_reader(),
|
reader_id: specs_world.write_storage::<C>().register_reader(),
|
||||||
inserted: BitSet::new(),
|
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 update if one exists.
|
||||||
|
//
|
||||||
|
// Note: presence of the id in these bitsets should be mutually exclusive
|
||||||
|
if self.modified.contains(id) {
|
||||||
|
storage
|
||||||
|
.get(entity)
|
||||||
|
.map(|comp| CompUpdateKind::Modified(P::from(comp.clone())))
|
||||||
|
} else if self.inserted.contains(id) {
|
||||||
|
storage
|
||||||
|
.get(entity)
|
||||||
|
.map(|comp| CompUpdateKind::Inserted(P::from(comp.clone())))
|
||||||
|
} else if self.removed.contains(id) {
|
||||||
|
Some(CompUpdateKind::Removed(P::Phantom::from(PhantomData::<C>)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
231
common/net/src/synced_components.rs
Normal file
231
common/net/src/synced_components.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
//! 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.
|
||||||
|
///
|
||||||
|
/// See [module](self) level docs for more details.
|
||||||
|
#[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 is used on the client,
|
||||||
|
// and if so what it is used for
|
||||||
|
player: Player,
|
||||||
|
// TODO: change this to `SyncFrom::ClientEntity` 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,
|
||||||
|
// TODO: this is used in combat rating calculation in voxygen but we can probably
|
||||||
|
// remove it from that and then see if it's used for anything else and try to move
|
||||||
|
// to only being synced for the client's entity.
|
||||||
|
skill_set: SkillSet,
|
||||||
|
|
||||||
|
// Synced to the client only for its own entity
|
||||||
|
|
||||||
|
combo: Combo,
|
||||||
|
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};
|
||||||
|
|
||||||
|
// We alias these because the identifier used for the
|
||||||
|
// component's type is reused as an enum variant name
|
||||||
|
// in the macro's that we pass to `synced_components!`.
|
||||||
|
//
|
||||||
|
// This is also the reason we need this inner module, since
|
||||||
|
// we can't just re-export all the types directly from `common::comp`.
|
||||||
|
pub type IsMount = Is<Mount>;
|
||||||
|
pub type IsRider = Is<Rider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export all the component types. So that uses of `synced_components!` outside this
|
||||||
|
// module can bring them into scope with a single glob import.
|
||||||
|
$(pub use inner::$type;)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pass `reexport_comps` macro to the "x macro" which will invoke it with a list
|
||||||
|
// of components.
|
||||||
|
//
|
||||||
|
// Note: this brings all these components into scope for the implementations
|
||||||
|
// below.
|
||||||
|
synced_components!(reexport_comps);
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// === NetSync implementations ===
|
||||||
|
// ===============================
|
||||||
|
|
||||||
|
use crate::sync::{NetSync, SyncFrom};
|
||||||
|
|
||||||
|
// These are synced from any entity within range.
|
||||||
|
|
||||||
|
impl NetSync for Body {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Stats {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Buffs {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Auras {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Energy {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Health {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
|
||||||
|
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::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for LightEmitter {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Item {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Scale {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Group {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for IsMount {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for IsRider {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Mass {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Density {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Collider {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Sticky {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for CharacterState {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Shockwave {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for BeamSegment {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Alignment {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Player {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for Inventory {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetSync for SkillSet {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are synced only from the client's own entity.
|
||||||
|
|
||||||
|
impl NetSync for Combo {
|
||||||
|
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;
|
||||||
|
}
|
@ -4,7 +4,7 @@ use specs::{Component, DerefFlaggedStorage};
|
|||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
/// Energy is represented by u32s within the module, but treated as a float by
|
/// Energy is represented by u32s within the module, but treated as a float by
|
||||||
/// the rest of the game.
|
/// the rest of the game.
|
||||||
// As a general rule, all input and output values to public functions should be
|
// As a general rule, all input and output values to public functions should be
|
||||||
@ -54,14 +54,31 @@ impl Energy {
|
|||||||
/// Returns the fraction of energy an entity has remaining
|
/// Returns the fraction of energy an entity has remaining
|
||||||
pub fn fraction(&self) -> f32 { self.current() / self.maximum().max(1.0) }
|
pub fn fraction(&self) -> f32 { self.current() / self.maximum().max(1.0) }
|
||||||
|
|
||||||
/// Updates the maximum value for energy
|
/// Calculates a new maximum value and returns it if the value differs from
|
||||||
pub fn update_maximum(&mut self, modifiers: comp::stats::StatsModifier) {
|
/// the current maximum.
|
||||||
|
///
|
||||||
|
/// Note: The returned value uses an internal format so don't expect it to
|
||||||
|
/// be useful for anything other than a parameter to
|
||||||
|
/// [`Self::update_maximum`].
|
||||||
|
pub fn needs_maximum_update(&self, modifiers: comp::stats::StatsModifier) -> Option<u32> {
|
||||||
let maximum = modifiers
|
let maximum = modifiers
|
||||||
.compute_maximum(self.base_max())
|
.compute_maximum(self.base_max())
|
||||||
.mul(Self::SCALING_FACTOR_FLOAT)
|
.mul(Self::SCALING_FACTOR_FLOAT)
|
||||||
// NaN does not need to be handled here as rust will automatically change to 0 when casting to u32
|
// NaN does not need to be handled here as rust will automatically change to 0 when casting to u32
|
||||||
.clamp(0.0, Self::MAX_SCALED_ENERGY as f32) as u32;
|
.clamp(0.0, Self::MAX_SCALED_ENERGY as f32) as u32;
|
||||||
|
|
||||||
|
(maximum != self.maximum).then(|| maximum)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the maximum value for energy.
|
||||||
|
///
|
||||||
|
/// Note: The accepted `u32` value is in the internal format of this type.
|
||||||
|
/// So attempting to pass values that weren't returned from
|
||||||
|
/// [`Self::needs_maximum_update`] can produce strange or unexpected
|
||||||
|
/// results.
|
||||||
|
pub fn update_internal_integer_maximum(&mut self, maximum: u32) {
|
||||||
self.maximum = maximum;
|
self.maximum = maximum;
|
||||||
|
// Clamp the current energy to enforce the current <= maximum invariant.
|
||||||
self.current = self.current.min(self.maximum);
|
self.current = self.current.min(self.maximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +90,29 @@ impl Health {
|
|||||||
/// Returns the fraction of health an entity has remaining
|
/// Returns the fraction of health an entity has remaining
|
||||||
pub fn fraction(&self) -> f32 { self.current() / self.maximum().max(1.0) }
|
pub fn fraction(&self) -> f32 { self.current() / self.maximum().max(1.0) }
|
||||||
|
|
||||||
/// Updates the maximum value for health
|
/// Calculates a new maximum value and returns it if the value differs from
|
||||||
pub fn update_maximum(&mut self, modifiers: comp::stats::StatsModifier) {
|
/// the current maximum.
|
||||||
|
///
|
||||||
|
/// Note: The returned value uses an internal format so don't expect it to
|
||||||
|
/// be useful for anything other than a parameter to
|
||||||
|
/// [`Self::update_maximum`].
|
||||||
|
pub fn needs_maximum_update(&self, modifiers: comp::stats::StatsModifier) -> Option<u32> {
|
||||||
let maximum = modifiers
|
let maximum = modifiers
|
||||||
.compute_maximum(self.base_max())
|
.compute_maximum(self.base_max())
|
||||||
.mul(Self::SCALING_FACTOR_FLOAT)
|
.mul(Self::SCALING_FACTOR_FLOAT)
|
||||||
// NaN does not need to be handled here as rust will automatically change to 0 when casting to u32
|
// NaN does not need to be handled here as rust will automatically change to 0 when casting to u32
|
||||||
.clamp(0.0, Self::MAX_SCALED_HEALTH as f32) as u32;
|
.clamp(0.0, Self::MAX_SCALED_HEALTH as f32) as u32;
|
||||||
|
|
||||||
|
(maximum != self.maximum).then(|| maximum)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the maximum value for health.
|
||||||
|
///
|
||||||
|
/// Note: The accepted `u32` value is in the internal format of this type.
|
||||||
|
/// So attempting to pass values that weren't returned from
|
||||||
|
/// [`Self::needs_maximum_update`] can produce strange or unexpected
|
||||||
|
/// results.
|
||||||
|
pub fn update_internal_integer_maximum(&mut self, maximum: u32) {
|
||||||
self.maximum = maximum;
|
self.maximum = maximum;
|
||||||
// Clamp the current health to enforce the current <= maximum invariant.
|
// Clamp the current health to enforce the current <= maximum invariant.
|
||||||
self.current = self.current.min(self.maximum);
|
self.current = self.current.min(self.maximum);
|
||||||
|
@ -30,6 +30,7 @@ impl StatsModifier {
|
|||||||
base_value * self.mult_mod + self.add_mod
|
base_value * self.mult_mod + self.add_mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: unused for now
|
||||||
pub fn update_maximum(&self) -> bool {
|
pub fn update_maximum(&self) -> bool {
|
||||||
self.add_mod.abs() > f32::EPSILON || (self.mult_mod - 1.0).abs() > f32::EPSILON
|
self.add_mod.abs() > f32::EPSILON || (self.mult_mod - 1.0).abs() > f32::EPSILON
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ pub struct JoinStruct<'a> {
|
|||||||
pub vel: &'a mut Vel,
|
pub vel: &'a mut Vel,
|
||||||
pub ori: &'a mut Ori,
|
pub ori: &'a mut Ori,
|
||||||
pub mass: &'a Mass,
|
pub mass: &'a Mass,
|
||||||
pub density: &'a mut Density,
|
pub density: FlaggedAccessMut<'a, &'a mut Density, Density>,
|
||||||
pub energy: FlaggedAccessMut<'a, &'a mut Energy, Energy>,
|
pub energy: FlaggedAccessMut<'a, &'a mut Energy, Energy>,
|
||||||
pub inventory: Option<&'a Inventory>,
|
pub inventory: Option<&'a Inventory>,
|
||||||
pub controller: &'a mut Controller,
|
pub controller: &'a mut Controller,
|
||||||
@ -171,7 +171,7 @@ impl<'a> JoinData<'a> {
|
|||||||
vel: j.vel,
|
vel: j.vel,
|
||||||
ori: j.ori,
|
ori: j.ori,
|
||||||
mass: j.mass,
|
mass: j.mass,
|
||||||
density: j.density,
|
density: &j.density,
|
||||||
energy: &j.energy,
|
energy: &j.energy,
|
||||||
inventory: j.inventory,
|
inventory: j.inventory,
|
||||||
controller: j.controller,
|
controller: j.controller,
|
||||||
|
@ -105,7 +105,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
vel,
|
vel,
|
||||||
ori,
|
ori,
|
||||||
mass,
|
mass,
|
||||||
mut density,
|
density,
|
||||||
energy,
|
energy,
|
||||||
inventory,
|
inventory,
|
||||||
controller,
|
controller,
|
||||||
@ -180,7 +180,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
vel,
|
vel,
|
||||||
ori,
|
ori,
|
||||||
mass,
|
mass,
|
||||||
density: &mut density,
|
density,
|
||||||
energy,
|
energy,
|
||||||
inventory,
|
inventory,
|
||||||
controller,
|
controller,
|
||||||
@ -238,15 +238,28 @@ impl Sys {
|
|||||||
mut state_update: StateUpdate,
|
mut state_update: StateUpdate,
|
||||||
output_events: &mut OutputEvents,
|
output_events: &mut OutputEvents,
|
||||||
) {
|
) {
|
||||||
// TODO: if checking equality is expensive use optional field in StateUpdate
|
// Here we check for equality with the previous value of these components before
|
||||||
|
// updating them so that the modification detection will not be
|
||||||
|
// triggered unnecessarily. This is important for minimizing updates
|
||||||
|
// sent to the clients (and thus keeping bandwidth usage down).
|
||||||
|
//
|
||||||
|
// TODO: if checking equality is expensive for char_state use optional field in
|
||||||
|
// StateUpdate
|
||||||
if *join.char_state != state_update.character {
|
if *join.char_state != state_update.character {
|
||||||
*join.char_state = state_update.character
|
*join.char_state = state_update.character
|
||||||
|
}
|
||||||
|
if *join.density != state_update.density {
|
||||||
|
*join.density = state_update.density
|
||||||
|
}
|
||||||
|
if *join.energy != state_update.energy {
|
||||||
|
*join.energy = state_update.energy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// These components use a different type of change detection.
|
||||||
*join.pos = state_update.pos;
|
*join.pos = state_update.pos;
|
||||||
*join.vel = state_update.vel;
|
*join.vel = state_update.vel;
|
||||||
*join.ori = state_update.ori;
|
*join.ori = state_update.ori;
|
||||||
*join.density = state_update.density;
|
|
||||||
*join.energy = state_update.energy;
|
|
||||||
join.controller
|
join.controller
|
||||||
.queued_inputs
|
.queued_inputs
|
||||||
.append(&mut state_update.queued_inputs);
|
.append(&mut state_update.queued_inputs);
|
||||||
|
@ -92,32 +92,23 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
let stat = stats;
|
let stat = stats;
|
||||||
|
|
||||||
let update_max_hp = {
|
if let Some(new_max) = health.needs_maximum_update(stat.max_health_modifiers) {
|
||||||
stat.max_health_modifiers.update_maximum()
|
// Only call this if we need to since mutable access will trigger sending an
|
||||||
|| (health.base_max() - health.maximum()).abs() > Health::HEALTH_EPSILON
|
// update to the client.
|
||||||
};
|
health.update_internal_integer_maximum(new_max);
|
||||||
|
|
||||||
if update_max_hp {
|
|
||||||
health.update_maximum(stat.max_health_modifiers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (change_energy, energy_mods) = {
|
|
||||||
// Calculates energy scaling from stats and inventory
|
// Calculates energy scaling from stats and inventory
|
||||||
let energy_mods = StatsModifier {
|
let energy_mods = StatsModifier {
|
||||||
add_mod: stat.max_energy_modifiers.add_mod
|
add_mod: stat.max_energy_modifiers.add_mod
|
||||||
+ combat::compute_max_energy_mod(inventory),
|
+ combat::compute_max_energy_mod(inventory),
|
||||||
mult_mod: stat.max_energy_modifiers.mult_mod,
|
mult_mod: stat.max_energy_modifiers.mult_mod,
|
||||||
};
|
};
|
||||||
(
|
|
||||||
energy_mods.update_maximum()
|
|
||||||
|| (energy.base_max() - energy.maximum()).abs() > Energy::ENERGY_EPSILON,
|
|
||||||
energy_mods,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// If modifier sufficiently different, mutably access energy
|
if let Some(new_max) = energy.needs_maximum_update(energy_mods) {
|
||||||
if change_energy {
|
// Only call this if we need to since mutable access will trigger sending an
|
||||||
energy.update_maximum(energy_mods);
|
// update to the client.
|
||||||
|
energy.update_internal_integer_maximum(new_max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ use crate::{
|
|||||||
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
|
presence::{Presence, RegionSubscription, RepositionOnChunkLoad},
|
||||||
rtsim::RtSim,
|
rtsim::RtSim,
|
||||||
state_ext::StateExt,
|
state_ext::StateExt,
|
||||||
sys::sentinel::{DeletedEntities, TrackedComps},
|
sys::sentinel::{DeletedEntities, TrackedStorages},
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "worldgen"))]
|
#[cfg(not(feature = "worldgen"))]
|
||||||
use common::grid::Grid;
|
use common::grid::Grid;
|
||||||
@ -447,7 +447,7 @@ impl Server {
|
|||||||
state.ecs_mut().write_resource::<TimeOfDay>().0 = settings.start_time;
|
state.ecs_mut().write_resource::<TimeOfDay>().0 = settings.start_time;
|
||||||
|
|
||||||
// Register trackers
|
// Register trackers
|
||||||
sys::sentinel::register_trackers(state.ecs_mut());
|
sys::sentinel::UpdateTrackers::register(state.ecs_mut());
|
||||||
|
|
||||||
state.ecs_mut().insert(DeletedEntities::default());
|
state.ecs_mut().insert(DeletedEntities::default());
|
||||||
|
|
||||||
@ -1025,7 +1025,7 @@ impl Server {
|
|||||||
)
|
)
|
||||||
.send(ServerInit::GameSync {
|
.send(ServerInit::GameSync {
|
||||||
// Send client their entity
|
// 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)
|
.create_entity_package(entity, None, None, None)
|
||||||
.expect(
|
.expect(
|
||||||
"We just created this entity as marked() (using create_entity_synced) so \
|
"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::{
|
use crate::{
|
||||||
client::Client,
|
client::Client,
|
||||||
presence::{Presence, RegionSubscription},
|
presence::{Presence, RegionSubscription},
|
||||||
@ -6,9 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
calendar::Calendar,
|
calendar::Calendar,
|
||||||
comp::{Collider, ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel},
|
comp::{Collider, ForceUpdate, InventoryUpdate, Last, Ori, Pos, Vel},
|
||||||
link::Is,
|
|
||||||
mounting::Rider,
|
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
region::{Event as RegionEvent, RegionMap},
|
region::{Event as RegionEvent, RegionMap},
|
||||||
resources::{PlayerPhysicsSettings, TimeOfDay},
|
resources::{PlayerPhysicsSettings, TimeOfDay},
|
||||||
@ -31,30 +29,25 @@ impl<'a> System<'a> for Sys {
|
|||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, Tick>,
|
Read<'a, Tick>,
|
||||||
|
Read<'a, PlayerPhysicsSettings>,
|
||||||
|
TrackedStorages<'a>,
|
||||||
ReadExpect<'a, TimeOfDay>,
|
ReadExpect<'a, TimeOfDay>,
|
||||||
ReadExpect<'a, Calendar>,
|
ReadExpect<'a, Calendar>,
|
||||||
ReadExpect<'a, RegionMap>,
|
ReadExpect<'a, RegionMap>,
|
||||||
ReadStorage<'a, Uid>,
|
ReadExpect<'a, UpdateTrackers>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Vel>,
|
ReadStorage<'a, Vel>,
|
||||||
ReadStorage<'a, Ori>,
|
ReadStorage<'a, Ori>,
|
||||||
ReadStorage<'a, Inventory>,
|
|
||||||
ReadStorage<'a, RegionSubscription>,
|
ReadStorage<'a, RegionSubscription>,
|
||||||
ReadStorage<'a, Presence>,
|
ReadStorage<'a, Presence>,
|
||||||
ReadStorage<'a, Collider>,
|
ReadStorage<'a, Client>,
|
||||||
WriteStorage<'a, Last<Pos>>,
|
WriteStorage<'a, Last<Pos>>,
|
||||||
WriteStorage<'a, Last<Vel>>,
|
WriteStorage<'a, Last<Vel>>,
|
||||||
WriteStorage<'a, Last<Ori>>,
|
WriteStorage<'a, Last<Ori>>,
|
||||||
ReadStorage<'a, Client>,
|
|
||||||
WriteStorage<'a, ForceUpdate>,
|
WriteStorage<'a, ForceUpdate>,
|
||||||
WriteStorage<'a, InventoryUpdate>,
|
WriteStorage<'a, InventoryUpdate>,
|
||||||
Write<'a, DeletedEntities>,
|
Write<'a, DeletedEntities>,
|
||||||
Write<'a, Vec<Outcome>>,
|
Write<'a, Vec<Outcome>>,
|
||||||
Read<'a, PlayerPhysicsSettings>,
|
|
||||||
ReadStorage<'a, Is<Rider>>,
|
|
||||||
ReadStorage<'a, Player>,
|
|
||||||
TrackedComps<'a>,
|
|
||||||
ReadTrackers<'a>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "entity_sync";
|
const NAME: &'static str = "entity_sync";
|
||||||
@ -66,33 +59,37 @@ impl<'a> System<'a> for Sys {
|
|||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
tick,
|
tick,
|
||||||
|
player_physics_settings,
|
||||||
|
tracked_storages,
|
||||||
time_of_day,
|
time_of_day,
|
||||||
calendar,
|
calendar,
|
||||||
region_map,
|
region_map,
|
||||||
uids,
|
trackers,
|
||||||
positions,
|
positions,
|
||||||
velocities,
|
velocities,
|
||||||
orientations,
|
orientations,
|
||||||
inventories,
|
|
||||||
subscriptions,
|
subscriptions,
|
||||||
presences,
|
presences,
|
||||||
colliders,
|
clients,
|
||||||
mut last_pos,
|
mut last_pos,
|
||||||
mut last_vel,
|
mut last_vel,
|
||||||
mut last_ori,
|
mut last_ori,
|
||||||
clients,
|
|
||||||
mut force_updates,
|
mut force_updates,
|
||||||
mut inventory_updates,
|
mut inventory_updates,
|
||||||
mut deleted_entities,
|
mut deleted_entities,
|
||||||
mut outcomes,
|
mut outcomes,
|
||||||
player_physics_settings,
|
|
||||||
is_rider,
|
|
||||||
players,
|
|
||||||
tracked_comps,
|
|
||||||
trackers,
|
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let tick = tick.0;
|
let tick = tick.0;
|
||||||
|
|
||||||
|
// Storages already provided in `TrackedStorages` that we need to use
|
||||||
|
// for other things besides change detection.
|
||||||
|
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
|
// To send entity updates
|
||||||
// 1. Iterate through regions
|
// 1. Iterate through regions
|
||||||
// 2. Iterate through region subscribers (ie clients)
|
// 2. Iterate through region subscribers (ie clients)
|
||||||
@ -161,7 +158,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.map(|pos| (pos, velocities.get(entity), orientations.get(entity)))
|
.map(|pos| (pos, velocities.get(entity), orientations.get(entity)))
|
||||||
.and_then(|(pos, vel, ori)| {
|
.and_then(|(pos, vel, ori)| {
|
||||||
tracked_comps.create_entity_package(
|
tracked_storages.create_entity_package(
|
||||||
entity,
|
entity,
|
||||||
Some(*pos),
|
Some(*pos),
|
||||||
vel.copied(),
|
vel.copied(),
|
||||||
@ -203,7 +200,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Sync tracked components
|
// Sync tracked components
|
||||||
// Get deleted entities in this region from DeletedEntities
|
// Get deleted entities in this region from DeletedEntities
|
||||||
let (entity_sync_package, comp_sync_package) = trackers.create_sync_packages(
|
let (entity_sync_package, comp_sync_package) = trackers.create_sync_packages(
|
||||||
&tracked_comps,
|
&tracked_storages,
|
||||||
region.entities(),
|
region.entities(),
|
||||||
deleted_entities_in_region,
|
deleted_entities_in_region,
|
||||||
);
|
);
|
||||||
@ -232,7 +229,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
for (_, entity, &uid, (&pos, last_pos), vel, ori, force_update, collider) in (
|
for (_, entity, &uid, (&pos, last_pos), vel, ori, force_update, collider) in (
|
||||||
region.entities(),
|
region.entities(),
|
||||||
&entities,
|
&entities,
|
||||||
&uids,
|
uids,
|
||||||
(&positions, last_pos.mask().maybe()),
|
(&positions, last_pos.mask().maybe()),
|
||||||
(&velocities, last_vel.mask().maybe()).maybe(),
|
(&velocities, last_vel.mask().maybe()).maybe(),
|
||||||
(&orientations, last_vel.mask().maybe()).maybe(),
|
(&orientations, last_vel.mask().maybe()).maybe(),
|
||||||
@ -353,13 +350,22 @@ impl<'a> System<'a> for Sys {
|
|||||||
// TODO: Sync clients that don't have a position?
|
// TODO: Sync clients that don't have a position?
|
||||||
|
|
||||||
// Sync inventories
|
// 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(
|
client.send_fallible(ServerGeneral::InventoryUpdate(
|
||||||
inventory.clone(),
|
inventory.clone(),
|
||||||
update.event(),
|
update.event(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync components that are only synced for the client's own 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
|
// Sync outcomes
|
||||||
for (presence, pos, client) in (presences.maybe(), positions.maybe(), &clients).join() {
|
for (presence, pos, client) in (presences.maybe(), positions.maybe(), &clients).join() {
|
||||||
let is_near = |o_pos: Vec3<f32>| {
|
let is_near = |o_pos: Vec3<f32>| {
|
||||||
|
@ -2,18 +2,14 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
item::{tool::AbilityMap, MaterialStatManifest},
|
item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Alignment, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState,
|
Ori, Pos, Vel,
|
||||||
Collider, Combo, Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass, Ori,
|
|
||||||
Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
|
|
||||||
},
|
},
|
||||||
link::Is,
|
|
||||||
mounting::{Mount, Rider},
|
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::{
|
use common_net::{
|
||||||
msg::EcsCompPacket,
|
msg::EcsCompPacket,
|
||||||
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, UpdateTracker, WorldSyncExt},
|
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, NetSync, SyncFrom, UpdateTracker},
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use specs::{
|
use specs::{
|
||||||
@ -28,53 +24,39 @@ use vek::*;
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for 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 NAME: &'static str = "sentinel";
|
||||||
const ORIGIN: Origin = Origin::Server;
|
const ORIGIN: Origin = Origin::Server;
|
||||||
const PHASE: Phase = Phase::Create;
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
fn run(_job: &mut Job<Self>, (comps, mut trackers): Self::SystemData) {
|
fn run(_job: &mut Job<Self>, (storages, mut trackers): Self::SystemData) {
|
||||||
record_changes(&comps, &mut trackers);
|
trackers.record_changes(&storages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Probably more difficult than it needs to be :p
|
/// Holds state like modified bitsets, modification event readers
|
||||||
#[derive(SystemData)]
|
macro_rules! trackers {
|
||||||
pub struct TrackedComps<'a> {
|
// Every place where we have `$( /* ... */ )*` will be repeated for each synced component.
|
||||||
|
($($component_name:ident: $component_type:ident,)*) => {
|
||||||
|
#[derive(SystemData)]
|
||||||
|
pub struct TrackedStorages<'a> {
|
||||||
|
// Uids are tracked to detect created entities that should be synced over the network.
|
||||||
|
// Additionally we need access to the uids when generating packets to send to the clients.
|
||||||
pub uid: ReadStorage<'a, Uid>,
|
pub uid: ReadStorage<'a, Uid>,
|
||||||
pub body: ReadStorage<'a, Body>,
|
$(pub $component_name: ReadStorage<'a, $component_type>,)*
|
||||||
pub player: ReadStorage<'a, Player>,
|
// TODO: these may be used to duplicate items when we attempt to remove
|
||||||
pub stats: ReadStorage<'a, Stats>,
|
// cloning them.
|
||||||
pub skill_set: ReadStorage<'a, SkillSet>,
|
pub _ability_map: ReadExpect<'a, AbilityMap>,
|
||||||
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
pub _msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
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>,
|
|
||||||
|
|
||||||
pub ability_map: ReadExpect<'a, AbilityMap>,
|
impl TrackedStorages<'_> {
|
||||||
pub msm: ReadExpect<'a, MaterialStatManifest>,
|
/// Create a package containing all the synced components for this entity. This is
|
||||||
}
|
/// used to initialized the entity's representation on the client (e.g. used when a new
|
||||||
impl<'a> TrackedComps<'a> {
|
/// entity is within the area synced to the client).
|
||||||
|
///
|
||||||
|
/// Note: This is only for components that are synced to the client for all entities.
|
||||||
pub fn create_entity_package(
|
pub fn create_entity_package(
|
||||||
&self,
|
&self,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
@ -84,108 +66,29 @@ impl<'a> TrackedComps<'a> {
|
|||||||
) -> Option<EntityPackage<EcsCompPacket>> {
|
) -> Option<EntityPackage<EcsCompPacket>> {
|
||||||
let uid = self.uid.get(entity).copied()?.0;
|
let uid = self.uid.get(entity).copied()?.0;
|
||||||
let mut comps = Vec::new();
|
let mut comps = Vec::new();
|
||||||
self.body.get(entity).copied().map(|c| comps.push(c.into()));
|
// NOTE: we could potentially include a bitmap indicating which components are present instead of tagging
|
||||||
self.player
|
// components with the type in order to save bandwidth
|
||||||
.get(entity)
|
//
|
||||||
.cloned()
|
// if the number of optional components sent is less than 1/8 of the number of component types then
|
||||||
.map(|c| comps.push(c.into()));
|
// then the suggested approach would no longer be favorable
|
||||||
self.stats
|
$(
|
||||||
.get(entity)
|
// Only add components that are synced from any entity.
|
||||||
.cloned()
|
if matches!(
|
||||||
.map(|c| comps.push(c.into()));
|
<$component_type as NetSync>::SYNC_FROM,
|
||||||
self.skill_set
|
SyncFrom::AnyEntity,
|
||||||
.get(entity)
|
) {
|
||||||
.cloned()
|
self
|
||||||
.map(|c| comps.push(c.into()));
|
.$component_name
|
||||||
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)
|
.get(entity)
|
||||||
|
// TODO: should duplicate rather than clone the item
|
||||||
|
//
|
||||||
|
// NetClone trait?
|
||||||
|
//
|
||||||
|
//.map(|item| item.duplicate(&self.ability_map, &self.msm))
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|c| comps.push(c.into()));
|
.map(|c| comps.push(c.into()));
|
||||||
|
}
|
||||||
|
)*
|
||||||
// Add untracked comps
|
// Add untracked comps
|
||||||
pos.map(|c| comps.push(c.into()));
|
pos.map(|c| comps.push(c.into()));
|
||||||
vel.map(|c| comps.push(c.into()));
|
vel.map(|c| comps.push(c.into()));
|
||||||
@ -193,235 +96,145 @@ impl<'a> TrackedComps<'a> {
|
|||||||
|
|
||||||
Some(EntityPackage { uid, comps })
|
Some(EntityPackage { uid, comps })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(SystemData)]
|
|
||||||
pub struct ReadTrackers<'a> {
|
/// Contains an [`UpdateTracker`] for every synced component (that uses this method of
|
||||||
pub uid: ReadExpect<'a, UpdateTracker<Uid>>,
|
/// change detection).
|
||||||
pub body: ReadExpect<'a, UpdateTracker<Body>>,
|
///
|
||||||
pub player: ReadExpect<'a, UpdateTracker<Player>>,
|
/// This should be inserted into the ecs as a Resource
|
||||||
pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
pub struct UpdateTrackers {
|
||||||
pub skill_set: ReadExpect<'a, UpdateTracker<SkillSet>>,
|
pub uid: UpdateTracker<Uid>,
|
||||||
pub active_abilities: ReadExpect<'a, UpdateTracker<ActiveAbilities>>,
|
$($component_name: UpdateTracker<$component_type>,)*
|
||||||
pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>,
|
}
|
||||||
pub auras: ReadExpect<'a, UpdateTracker<Auras>>,
|
|
||||||
pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
|
impl UpdateTrackers {
|
||||||
pub combo: ReadExpect<'a, UpdateTracker<Combo>>,
|
/// Constructs the update trackers and inserts it into the world as a resource.
|
||||||
pub health: ReadExpect<'a, UpdateTracker<Health>>,
|
///
|
||||||
pub poise: ReadExpect<'a, UpdateTracker<Poise>>,
|
/// Components that will be synced must already be registered.
|
||||||
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
|
pub fn register(world: &mut specs::World) {
|
||||||
pub light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
|
let trackers = UpdateTrackers {
|
||||||
pub inventory: ReadExpect<'a, UpdateTracker<Inventory>>,
|
uid: UpdateTracker::<Uid>::new(&world),
|
||||||
pub item: ReadExpect<'a, UpdateTracker<Item>>,
|
$($component_name: UpdateTracker::<$component_type>::new(&world),)*
|
||||||
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
};
|
||||||
pub is_mount: ReadExpect<'a, UpdateTracker<Is<Mount>>>,
|
|
||||||
pub is_rider: ReadExpect<'a, UpdateTracker<Is<Rider>>>,
|
world.insert(trackers);
|
||||||
pub group: ReadExpect<'a, UpdateTracker<Group>>,
|
// TODO: if we held copies of components for doing diffing, the components that hold that data could be registered here
|
||||||
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
}
|
||||||
pub density: ReadExpect<'a, UpdateTracker<Density>>,
|
|
||||||
pub collider: ReadExpect<'a, UpdateTracker<Collider>>,
|
/// Records updates to components that are provided from the tracked storages as a series of events into bitsets
|
||||||
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
/// that can later be joined on.
|
||||||
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
fn record_changes(&mut self, comps: &TrackedStorages) {
|
||||||
pub shockwave: ReadExpect<'a, UpdateTracker<Shockwave>>,
|
self.uid.record_changes(&comps.uid);
|
||||||
pub beam_segment: ReadExpect<'a, UpdateTracker<BeamSegment>>,
|
$(
|
||||||
pub alignment: ReadExpect<'a, UpdateTracker<Alignment>>,
|
self.$component_name.record_changes(&comps.$component_name);
|
||||||
}
|
)*
|
||||||
impl<'a> ReadTrackers<'a> {
|
|
||||||
|
// Enable for logging of counts of component update events.
|
||||||
|
const LOG_COUNTS: bool = false;
|
||||||
|
// Plotting counts via tracy. Env var provided to toggle on so there's no need to
|
||||||
|
// recompile if you are already have a tracy build.
|
||||||
|
let plot_counts = common_base::TRACY_ENABLED && matches!(std::env::var("PLOT_UPDATE_COUNTS").as_deref(), Ok("1"));
|
||||||
|
|
||||||
|
macro_rules! log_counts {
|
||||||
|
($comp:ident, $name:expr) => {
|
||||||
|
if LOG_COUNTS || plot_counts {
|
||||||
|
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();
|
||||||
|
|
||||||
|
if plot_counts {
|
||||||
|
let sum = inserted + modified + removed;
|
||||||
|
common_base::plot!(concat!($name, "updates"), sum as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
if LOG_COUNTS {
|
||||||
|
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'));)*
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`EntitySyncPackage`] and a [`CompSyncPackage`] to provide updates
|
||||||
|
/// for the set entities specified by the provided filter (e.g. for a region).
|
||||||
|
///
|
||||||
|
/// A deleted entities must be externally constructed and provided here.
|
||||||
pub fn create_sync_packages(
|
pub fn create_sync_packages(
|
||||||
&self,
|
&self,
|
||||||
comps: &TrackedComps,
|
comps: &TrackedStorages,
|
||||||
filter: impl Join + Copy,
|
filter: impl Join + Copy,
|
||||||
deleted_entities: Vec<u64>,
|
deleted_entities: Vec<u64>,
|
||||||
) -> (EntitySyncPackage, CompSyncPackage<EcsCompPacket>) {
|
) -> (EntitySyncPackage, CompSyncPackage<EcsCompPacket>) {
|
||||||
let entity_sync_package =
|
let entity_sync_package =
|
||||||
EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities);
|
EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities);
|
||||||
let comp_sync_package = CompSyncPackage::new()
|
let mut 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)
|
if matches!(
|
||||||
.with_component(&comps.uid, &*self.skill_set, &comps.skill_set, filter)
|
<$component_type as NetSync>::SYNC_FROM,
|
||||||
.with_component(
|
SyncFrom::AnyEntity,
|
||||||
|
) {
|
||||||
|
comp_sync_package.add_component_updates(
|
||||||
&comps.uid,
|
&comps.uid,
|
||||||
&*self.active_abilities,
|
&self.$component_name,
|
||||||
&comps.active_abilities,
|
&comps.$component_name,
|
||||||
filter,
|
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);
|
|
||||||
|
|
||||||
(entity_sync_package, comp_sync_package)
|
(entity_sync_package, 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) {
|
/// Create sync package for components that are only synced for the client's entity.
|
||||||
// Update trackers
|
pub fn create_sync_from_client_package(
|
||||||
trackers.uid.record_changes(&comps.uid);
|
&self,
|
||||||
trackers.body.record_changes(&comps.body);
|
comps: &TrackedStorages,
|
||||||
trackers.player.record_changes(&comps.player);
|
entity: specs::Entity,
|
||||||
trackers.stats.record_changes(&comps.stats);
|
) -> CompSyncPackage<EcsCompPacket> {
|
||||||
trackers.skill_set.record_changes(&comps.skill_set);
|
// TODO: this type repeats the entity uid for each component but
|
||||||
trackers
|
// we know they will all be the same here, using it for now for
|
||||||
.active_abilities
|
// convenience but it could help to make a specific type for this
|
||||||
.record_changes(&comps.active_abilities);
|
// later.
|
||||||
trackers.buffs.record_changes(&comps.buffs);
|
let mut comp_sync_package = CompSyncPackage::new();
|
||||||
trackers.auras.record_changes(&comps.auras);
|
|
||||||
trackers.energy.record_changes(&comps.energy);
|
let uid = match comps.uid.get(entity) {
|
||||||
trackers.combo.record_changes(&comps.combo);
|
Some(uid) => (*uid).into(),
|
||||||
trackers.health.record_changes(&comps.health);
|
// Return empty package if we can't get uid for this entity
|
||||||
trackers.poise.record_changes(&comps.poise);
|
None => return comp_sync_package,
|
||||||
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");
|
if matches!(
|
||||||
log_counts!(buffs, "Buffs");
|
<$component_type as NetSync>::SYNC_FROM,
|
||||||
log_counts!(auras, "Auras");
|
SyncFrom::ClientEntity,
|
||||||
log_counts!(player, "Players");
|
) {
|
||||||
log_counts!(stats, "Stats");
|
comp_sync_package.add_component_update(
|
||||||
log_counts!(skill_set, "SkillSet");
|
&self.$component_name,
|
||||||
log_counts!(active_abilities, "ActiveAbilities");
|
&comps.$component_name,
|
||||||
log_counts!(energy, "Energies");
|
uid,
|
||||||
log_counts!(combo, "Combos");
|
entity,
|
||||||
log_vounts!(health, "Healths");
|
);
|
||||||
log_vounts!(poise, "Poises");
|
}
|
||||||
log_counts!(light_emitter, "Light emitters");
|
)*
|
||||||
log_counts!(item, "Items");
|
|
||||||
log_counts!(scale, "Scales");
|
comp_sync_package
|
||||||
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) {
|
// Import all the component types so they will be available when expanding the
|
||||||
world.register_tracker::<Uid>();
|
// macro below.
|
||||||
world.register_tracker::<Body>();
|
use common_net::synced_components::*;
|
||||||
world.register_tracker::<Player>();
|
// Pass `trackers!` macro to this "x macro" which will invoke it with a list
|
||||||
world.register_tracker::<Stats>();
|
// of components. This will declare the types defined in the macro above.
|
||||||
world.register_tracker::<SkillSet>();
|
common_net::synced_components!(trackers);
|
||||||
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>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deleted entities grouped by region
|
/// Deleted entities grouped by region
|
||||||
pub struct DeletedEntities {
|
pub struct DeletedEntities {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::sentinel::{DeletedEntities, TrackedComps};
|
use super::sentinel::{DeletedEntities, TrackedStorages};
|
||||||
use crate::{
|
use crate::{
|
||||||
client::Client,
|
client::Client,
|
||||||
presence::{self, Presence, RegionSubscription},
|
presence::{self, Presence, RegionSubscription},
|
||||||
@ -34,7 +34,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Client>,
|
ReadStorage<'a, Client>,
|
||||||
WriteStorage<'a, RegionSubscription>,
|
WriteStorage<'a, RegionSubscription>,
|
||||||
Read<'a, DeletedEntities>,
|
Read<'a, DeletedEntities>,
|
||||||
TrackedComps<'a>,
|
TrackedStorages<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "subscription";
|
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 region_map = world.read_resource::<RegionMap>();
|
||||||
let tracked_comps = TrackedComps::fetch(world);
|
let tracked_comps = TrackedStorages::fetch(world);
|
||||||
for key in ®ions {
|
for key in ®ions {
|
||||||
if let Some(region) = region_map.get(*key) {
|
if let Some(region) = region_map.get(*key) {
|
||||||
(
|
(
|
||||||
|
Reference in New Issue
Block a user