Move sync code into common submodule

This commit is contained in:
Imbris 2019-11-24 15:12:03 -05:00 committed by Imbris
parent 609e0f23bf
commit 71cce03f29
27 changed files with 684 additions and 125 deletions

12
Cargo.lock generated
View File

@ -3194,16 +3194,6 @@ dependencies = [
"specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sphynx"
version = "0.1.0"
dependencies = [
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
"specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sum_type 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "spin"
version = "0.5.2"
@ -3598,7 +3588,7 @@ dependencies = [
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)",
"specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)",
"sphynx 0.1.0",
"sum_type 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vek 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -14,8 +14,8 @@ use common::{
ServerError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG,
},
net::PostBox,
sphynx::WorldSyncExt,
state::{State, Uid},
state::State,
sync::{Uid, WorldSyncExt},
terrain::{block::Block, TerrainChunk, TerrainChunkSize},
vol::RectVolSize,
ChatType,

View File

@ -5,8 +5,6 @@ authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>", "Maciej Ćwięka <mc
edition = "2018"
[dependencies]
#sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"], rev = "ac4adf54d181339a789907acd27f61fe81daa455" }
sphynx = { path = "../../sphynx", features = ["serde1"] }
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
specs = { version = "0.14.2", features = ["serde", "nightly"] }
@ -31,6 +29,7 @@ parking_lot = "0.9.0"
crossbeam = "=0.7.2"
notify = "5.0.0-pre.1"
indexmap = "1.3.0"
sum_type = "0.2.0"
# TODO: remove when upgrading to specs 0.15
hibitset = "0.5.3"

View File

@ -1,6 +1,6 @@
use crate::sync::Uid;
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use sphynx::Uid;
use std::time::Duration;
use vek::*;

View File

@ -1,4 +1,4 @@
use crate::state::Uid;
use crate::sync::Uid;
use specs::{Component, FlaggedStorage, NullStorage};
use specs_idvs::IDVStorage;
use vek::*;

View File

@ -1,5 +1,4 @@
use crate::comp;
use crate::state::Uid;
use crate::{comp, sync::Uid};
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use std::time::Duration;

View File

@ -1,4 +1,4 @@
use crate::{comp, state::Uid};
use crate::{comp, sync::Uid};
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;

View File

@ -1,9 +1,8 @@
use crate::comp;
use crate::{comp, sync::Uid};
use comp::item::Tool;
use parking_lot::Mutex;
use serde::Deserialize;
use specs::Entity as EcsEntity;
use sphynx::Uid;
use std::{collections::VecDeque, ops::DerefMut};
use vek::*;

View File

@ -7,9 +7,6 @@ extern crate serde_derive;
#[macro_use]
extern crate log;
// Re-export sphynx
pub use sphynx;
pub mod assets;
pub mod astar;
pub mod clock;
@ -23,6 +20,7 @@ pub mod pathfinding;
pub mod ray;
pub mod region;
pub mod state;
pub mod sync;
pub mod sys;
pub mod terrain;
pub mod util;

View File

@ -1,7 +1,7 @@
use crate::{comp, state};
use crate::{comp, state, sync};
use serde_derive::{Deserialize, Serialize};
use sphynx::sum_type;
use std::marker::PhantomData;
use sum_type::sum_type;
// Automatically derive From<T> for EcsResPacket
// for each variant EcsResPacket::T(T).
@ -12,11 +12,11 @@ sum_type! {
TimeOfDay(state::TimeOfDay),
}
}
impl sphynx::ResPacket for EcsResPacket {
impl sync::ResPacket for EcsResPacket {
fn apply(self, world: &specs::World) {
match self {
EcsResPacket::Time(time) => sphynx::handle_res_update(time, world),
EcsResPacket::TimeOfDay(time_of_day) => sphynx::handle_res_update(time_of_day, world),
EcsResPacket::Time(time) => sync::handle_res_update(time, world),
EcsResPacket::TimeOfDay(time_of_day) => sync::handle_res_update(time_of_day, world),
}
}
}
@ -66,72 +66,68 @@ sum_type! {
Sticky(PhantomData<comp::Sticky>),
}
}
impl sphynx::CompPacket for EcsCompPacket {
impl sync::CompPacket for EcsCompPacket {
type Phantom = EcsCompPhantom;
fn apply_insert(self, entity: specs::Entity, world: &specs::World) {
match self {
EcsCompPacket::Pos(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Vel(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Ori(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Body(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Player(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::CanBuild(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Stats(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::LightEmitter(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Item(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Scale(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::MountState(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Mounting(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Mass(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Projectile(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Gravity(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Sticky(comp) => sphynx::handle_insert(comp, entity, world),
EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world),
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::LightEmitter(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::MountState(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Projectile(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
}
}
fn apply_modify(self, entity: specs::Entity, world: &specs::World) {
match self {
EcsCompPacket::Pos(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Vel(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Ori(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Body(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Player(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::CanBuild(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Stats(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::LightEmitter(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Item(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Scale(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::MountState(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Mounting(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Mass(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Projectile(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Gravity(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Sticky(comp) => sphynx::handle_modify(comp, entity, world),
EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world),
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::LightEmitter(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::MountState(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Projectile(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
}
}
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
match phantom {
EcsCompPhantom::Pos(_) => sphynx::handle_remove::<comp::Pos>(entity, world),
EcsCompPhantom::Vel(_) => sphynx::handle_remove::<comp::Vel>(entity, world),
EcsCompPhantom::Ori(_) => sphynx::handle_remove::<comp::Ori>(entity, world),
EcsCompPhantom::Body(_) => sphynx::handle_remove::<comp::Body>(entity, world),
EcsCompPhantom::Player(_) => sphynx::handle_remove::<comp::Player>(entity, world),
EcsCompPhantom::CanBuild(_) => sphynx::handle_remove::<comp::CanBuild>(entity, world),
EcsCompPhantom::Stats(_) => sphynx::handle_remove::<comp::Stats>(entity, world),
EcsCompPhantom::Pos(_) => sync::handle_remove::<comp::Pos>(entity, world),
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
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::LightEmitter(_) => {
sphynx::handle_remove::<comp::LightEmitter>(entity, world)
sync::handle_remove::<comp::LightEmitter>(entity, world)
}
EcsCompPhantom::Item(_) => sphynx::handle_remove::<comp::Item>(entity, world),
EcsCompPhantom::Scale(_) => sphynx::handle_remove::<comp::Scale>(entity, world),
EcsCompPhantom::MountState(_) => {
sphynx::handle_remove::<comp::MountState>(entity, world)
}
EcsCompPhantom::Mounting(_) => sphynx::handle_remove::<comp::Mounting>(entity, world),
EcsCompPhantom::Mass(_) => sphynx::handle_remove::<comp::Mass>(entity, world),
EcsCompPhantom::Projectile(_) => {
sphynx::handle_remove::<comp::Projectile>(entity, world)
}
EcsCompPhantom::Gravity(_) => sphynx::handle_remove::<comp::Gravity>(entity, world),
EcsCompPhantom::Sticky(_) => sphynx::handle_remove::<comp::Sticky>(entity, world),
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
EcsCompPhantom::Mounting(_) => sync::handle_remove::<comp::Mounting>(entity, world),
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
EcsCompPhantom::Projectile(_) => sync::handle_remove::<comp::Projectile>(entity, world),
EcsCompPhantom::Gravity(_) => sync::handle_remove::<comp::Gravity>(entity, world),
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
}
}
}

View File

@ -1,6 +1,6 @@
use super::{ClientState, EcsCompPacket, EcsResPacket};
use crate::{
comp,
comp, sync,
terrain::{Block, TerrainChunk},
ChatType,
};
@ -26,7 +26,7 @@ pub struct ServerInfo {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ServerMsg {
InitialSync {
ecs_state: sphynx::StatePackage<EcsCompPacket, EcsResPacket>,
ecs_state: sync::StatePackage<EcsCompPacket, EcsResPacket>,
entity_uid: u64,
server_info: ServerInfo,
// world_map: Vec2<usize>, /*, Vec<u32>)*/
@ -40,9 +40,9 @@ pub enum ServerMsg {
message: String,
},
SetPlayerEntity(u64),
EcsSync(sphynx::SyncPackage<EcsCompPacket>),
EcsResSync(sphynx::ResSyncPackage<EcsResPacket>),
CreateEntity(sphynx::EntityPackage<EcsCompPacket>),
EcsSync(sync::SyncPackage<EcsCompPacket>),
EcsResSync(sync::ResSyncPackage<EcsResPacket>),
CreateEntity(sync::EntityPackage<EcsCompPacket>),
DeleteEntity(u64),
EntityPos {
entity: u64,

View File

@ -1,10 +1,8 @@
// Reexports
pub use sphynx::Uid;
use crate::{
comp,
event::{EventBus, LocalEvent, ServerEvent, SfxEventItem},
region::RegionMap,
sync::WorldSyncExt,
sys,
terrain::{Block, TerrainChunk, TerrainGrid},
vol::WriteVol,
@ -17,7 +15,6 @@ use specs::{
storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage},
Component, DispatcherBuilder, Entity as EcsEntity,
};
use sphynx::WorldSyncExt;
use std::{sync::Arc, time::Duration};
use vek::*;
@ -103,20 +100,6 @@ impl Default for State {
}
impl State {
// Create a new `State` from an ECS state package.
/*pub fn from_state_package(
state_package: sphynx::StatePackage<EcsCompPacket, EcsResPacket>,
) -> Self {
Self {
ecs: sphynx::World::from_state_package(
specs::World::new(),
Self::setup_sphynx_world,
state_package,
),
thread_pool: Arc::new(ThreadPoolBuilder::new().build().unwrap()),
}
}*/
/// Creates ecs world and registers all the common components and resources
// TODO: Split up registering into server and client (e.g. move EventBus<ServerEvent> to the server)
fn setup_ecs_world() -> specs::World {

14
common/src/sync/mod.rs Normal file
View File

@ -0,0 +1,14 @@
// Note: Currently only one-way sync is supported until a usecase for two-way sync arises
mod packet;
mod sync_ext;
mod track;
mod uid;
// Reexports
pub use packet::{
handle_insert, handle_modify, handle_remove, handle_res_update, CompPacket, EntityPackage,
ResPacket, ResSyncPackage, StatePackage, SyncPackage,
};
pub use sync_ext::WorldSyncExt;
pub use track::{Tracker, UpdateTracker};
pub use uid::{Uid, UidAllocator};

167
common/src/sync/packet.rs Normal file
View File

@ -0,0 +1,167 @@
use super::{
track::{Tracker, UpdateTracker},
uid::Uid,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use specs::{shred::Resource, Component, Entity, Join, ReadStorage, World};
use std::{
convert::{TryFrom, TryInto},
fmt::Debug,
marker::PhantomData,
};
pub trait CompPacket: Clone + Debug + Send + 'static {
type Phantom: Clone + Debug + Serialize + DeserializeOwned;
fn apply_insert(self, entity: Entity, world: &World);
fn apply_modify(self, entity: Entity, world: &World);
fn apply_remove(phantom: Self::Phantom, entity: Entity, world: &World);
}
pub trait ResPacket: Clone + Debug + Send + 'static {
fn apply(self, world: &World);
}
/// Useful for implementing CompPacket trait
pub fn handle_insert<C: Component>(comp: C, entity: Entity, world: &World) {
let _ = world.write_storage::<C>().insert(entity, comp);
}
/// Useful for implementing CompPacket trait
pub fn handle_modify<C: Component>(comp: C, entity: Entity, world: &World) {
let _ = world
.write_storage::<C>()
.get_mut(entity)
.map(|c| *c = comp);
}
/// Useful for implementing CompPacket trait
pub fn handle_remove<C: Component>(entity: Entity, world: &World) {
let _ = world.write_storage::<C>().remove(entity);
}
/// Useful for implementing ResPacket trait
pub fn handle_res_update<R: Resource>(res: R, world: &World) {
*world.write_resource::<R>() = res;
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum CompUpdateKind<P: CompPacket> {
Inserted(P),
Modified(P),
Removed(P::Phantom),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EntityPackage<P: CompPacket>(pub u64, pub Vec<P>);
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StatePackage<P: CompPacket, R: ResPacket> {
pub entities: Vec<EntityPackage<P>>,
pub resources: Vec<R>,
}
impl<P: CompPacket, R: ResPacket> Default for StatePackage<P, R> {
fn default() -> Self {
Self {
entities: Vec::new(),
resources: Vec::new(),
}
}
}
impl<P: CompPacket, R: ResPacket> StatePackage<P, R> {
pub fn new() -> Self {
Self::default()
}
pub fn with_entities<C: Component + Clone + Send + Sync>(
mut self,
mut entities: Vec<EntityPackage<P>>,
) -> Self {
self.entities.append(&mut entities);
self
}
pub fn with_entity(mut self, entry: EntityPackage<P>) -> Self {
self.entities.push(entry);
self
}
pub fn with_res<C: Resource + Clone + Send + Sync>(mut self, res: &C) -> Self
where
R: From<C>,
{
self.resources.push(R::from(res.clone()));
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SyncPackage<P: CompPacket> {
pub comp_updates: Vec<(u64, CompUpdateKind<P>)>,
pub created_entities: Vec<u64>,
pub deleted_entities: Vec<u64>,
}
impl<P: CompPacket> SyncPackage<P> {
pub fn new<'a>(
uids: &ReadStorage<'a, Uid>,
uid_tracker: &UpdateTracker<Uid>,
filter: impl Join + Copy,
) -> Self {
// Add created and deleted entities
let created_entities = (uids, filter, uid_tracker.inserted())
.join()
.map(|(uid, _, _)| (*uid).into())
.collect();
// TODO: handle modified uid?
//created_entities.append(&mut (uids, filter, uid_tracker.inserted()).join().map(|(uid, _, _)| uid).collect());
let deleted_entities = (uids, filter, uid_tracker.removed())
.join()
.map(|(uid, _, _)| (*uid).into())
.collect();
Self {
comp_updates: Vec::new(),
created_entities,
deleted_entities,
}
}
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
mut self,
uids: &ReadStorage<'a, Uid>,
uid_tracker: &UpdateTracker<Uid>,
tracker: &impl Tracker<C, P>,
storage: &ReadStorage<'a, C>,
filter: impl Join + Copy,
) -> Self
where
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
P::Phantom: TryInto<PhantomData<C>>,
C::Storage: specs::storage::Tracked,
{
tracker.get_updates_for(
uids,
storage,
// Don't include updates for deleted entities
(filter, &!uid_tracker.removed()),
&mut self.comp_updates,
);
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ResSyncPackage<R: ResPacket> {
pub resources: Vec<R>,
}
impl<R: ResPacket> ResSyncPackage<R> {
pub fn new() -> Self {
Self {
resources: Vec::new(),
}
}
pub fn with_res<C: Resource + Clone + Send + Sync>(mut self, res: &C) -> Self
where
R: From<C>,
{
self.resources.push(R::from(res.clone()));
self
}
}

178
common/src/sync/sync_ext.rs Normal file
View File

@ -0,0 +1,178 @@
use super::{
packet::{
CompPacket, CompUpdateKind, EntityPackage, ResPacket, ResSyncPackage, StatePackage,
SyncPackage,
},
track::UpdateTracker,
uid::{Uid, UidAllocator},
};
use specs::{
saveload::{MarkedBuilder, MarkerAllocator},
world::Builder,
};
pub trait WorldSyncExt {
fn register_sync_marker(&mut self);
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
where
C::Storage: Default + specs::storage::Tracked;
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
where
C::Storage: Default + specs::storage::Tracked;
fn create_entity_synced(&mut self) -> specs::EntityBuilder;
fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid>;
fn entity_from_uid(&self, uid: u64) -> Option<specs::Entity>;
fn apply_entity_package<P: CompPacket>(&mut self, entity_package: EntityPackage<P>);
fn apply_state_package<P: CompPacket, R: ResPacket>(
&mut self,
state_package: StatePackage<P, R>,
);
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>);
fn apply_res_sync_package<R: ResPacket>(&mut self, package: ResSyncPackage<R>);
}
impl WorldSyncExt for specs::World {
fn register_sync_marker(&mut self) {
self.register_synced::<Uid>();
self.add_resource(UidAllocator::new());
}
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
where
// P: From<C>,
// C: TryFrom<P, Error = InvalidType>,
// P::Phantom: From<PhantomData<C>>,
// P::Phantom: TryInto<PhantomData<C>>,
C::Storage: Default + specs::storage::Tracked,
{
self.register::<C>();
self.register_tracker::<C>();
}
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
where
// P: From<C>,
// C: TryFrom<P, Error = InvalidType>,
// P::Phantom: From<PhantomData<C>>,
// P::Phantom: TryInto<PhantomData<C>>,
C::Storage: Default + specs::storage::Tracked,
{
let tracker = UpdateTracker::<C>::new(self);
self.add_resource(tracker);
}
/*fn insert_synced<C: specs::shred::Resource + Clone + Send + Sync>(&mut self, res: C)
//where
// R: From<C>,
// C: TryFrom<R>,
{
self.add_resource::<C>(res);
self.res_trackers.insert(ResUpdateTracker::<C>::new());
}*/
fn create_entity_synced(&mut self) -> specs::EntityBuilder {
self.create_entity().marked::<super::Uid>()
}
/// Get the UID of an entity
fn uid_from_entity(&self, entity: specs::Entity) -> Option<Uid> {
self.read_storage::<Uid>().get(entity).copied()
}
/// Get the UID of an entity
fn entity_from_uid(&self, uid: u64) -> Option<specs::Entity> {
self.read_resource::<UidAllocator>()
.retrieve_entity_internal(uid)
}
fn apply_entity_package<P: CompPacket>(&mut self, entity_package: EntityPackage<P>) {
let EntityPackage(entity_uid, packets) = entity_package;
let entity = create_entity_with_uid(self, entity_uid);
for packet in packets {
packet.apply_insert(entity, self)
}
}
fn apply_state_package<P: CompPacket, R: ResPacket>(
&mut self,
state_package: StatePackage<P, R>,
) {
let StatePackage {
entities,
resources,
} = state_package;
// Apply state package resources
for res_packet in resources {
res_packet.apply(self);
}
// Apply state package entities
for entity_package in entities {
self.apply_entity_package(entity_package);
}
// Initialize entities
//specs_world.maintain();
}
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>) {
// Take ownership of the fields
let SyncPackage {
comp_updates,
created_entities,
deleted_entities,
} = package;
// Attempt to create entities
for entity_uid in created_entities {
create_entity_with_uid(self, entity_uid);
}
// Update components
for (entity_uid, update) in comp_updates {
if let Some(entity) = self
.read_resource::<UidAllocator>()
.retrieve_entity_internal(entity_uid)
{
match update {
CompUpdateKind::Inserted(packet) => {
packet.apply_insert(entity, self);
}
CompUpdateKind::Modified(packet) => {
packet.apply_modify(entity, self);
}
CompUpdateKind::Removed(phantom) => {
P::apply_remove(phantom, entity, self);
}
}
}
}
// Attempt to delete entities that were marked for deletion
for entity_uid in deleted_entities {
let entity = self
.read_resource::<UidAllocator>()
.retrieve_entity_internal(entity_uid);
if let Some(entity) = entity {
let _ = self.delete_entity(entity);
}
}
}
fn apply_res_sync_package<R: ResPacket>(&mut self, package: ResSyncPackage<R>) {
// Update resources
for res_packet in package.resources {
res_packet.apply(self);
}
}
}
// Private utilities
fn create_entity_with_uid(specs_world: &mut specs::World, entity_uid: u64) -> specs::Entity {
let existing_entity = specs_world
.read_resource::<UidAllocator>()
.retrieve_entity_internal(entity_uid);
existing_entity.unwrap_or_else(|| specs_world.create_entity_synced().build())
}

144
common/src/sync/track.rs Normal file
View File

@ -0,0 +1,144 @@
use super::{
packet::{CompPacket, CompUpdateKind},
uid::Uid,
};
use specs::{BitSet, Component, Entity, Join, ReadStorage, World};
use std::{
convert::{TryFrom, TryInto},
marker::PhantomData,
};
pub trait Tracker<C: Component + Clone + Send + Sync, P: CompPacket>: Send + 'static
where
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
P::Phantom: TryInto<PhantomData<C>>,
C::Storage: specs::storage::Tracked,
{
fn add_packet_for<'a>(
&self,
storage: &specs::ReadStorage<'a, C>,
entity: specs::Entity,
packets: &mut Vec<P>,
);
fn get_updates_for<'a>(
&self,
uids: &specs::ReadStorage<'a, Uid>,
storage: &specs::ReadStorage<'a, C>,
filter: impl Join + Copy,
buf: &mut Vec<(u64, CompUpdateKind<P>)>,
);
}
pub struct UpdateTracker<C: Component> {
reader_id: specs::ReaderId<specs::storage::ComponentEvent>,
inserted: BitSet,
modified: BitSet,
removed: BitSet,
phantom: PhantomData<C>,
}
impl<C: Component> UpdateTracker<C>
where
C::Storage: specs::storage::Tracked,
{
pub fn new(specs_world: &mut World) -> Self {
Self {
reader_id: specs_world.write_storage::<C>().register_reader(),
inserted: BitSet::new(),
modified: BitSet::new(),
removed: BitSet::new(),
phantom: PhantomData,
}
}
pub fn inserted(&self) -> &BitSet {
&self.inserted
}
pub fn modified(&self) -> &BitSet {
&self.modified
}
pub fn removed(&self) -> &BitSet {
&self.removed
}
pub fn record_changes<'a>(&mut self, storage: &specs::ReadStorage<'a, C>) {
self.inserted.clear();
self.modified.clear();
self.removed.clear();
for event in storage.channel().read(&mut self.reader_id) {
match event {
specs::storage::ComponentEvent::Inserted(id) => {
// If previously removed/modified we don't need to know that anymore
self.removed.remove(*id);
self.modified.remove(*id);
self.inserted.add(*id);
}
specs::storage::ComponentEvent::Modified(id) => {
// We don't care about modification if the component was just added or was
// removed
if !self.removed.contains(*id) && !self.inserted.contains(*id) {
self.modified.add(*id);
}
}
specs::storage::ComponentEvent::Removed(id) => {
// Don't need to know that it was inserted/modified if it was subsequently removed
self.inserted.remove(*id);
self.modified.remove(*id);
self.removed.add(*id);
}
};
}
}
}
impl<C: Component + Clone + Send + Sync, P: CompPacket> Tracker<C, P> for UpdateTracker<C>
where
P: From<C>,
C: TryFrom<P>,
P::Phantom: From<PhantomData<C>>,
P::Phantom: TryInto<PhantomData<C>>,
C::Storage: specs::storage::Tracked,
{
fn add_packet_for<'a>(
&self,
storage: &ReadStorage<'a, C>,
entity: Entity,
packets: &mut Vec<P>,
) {
if let Some(comp) = storage.get(entity) {
packets.push(P::from(comp.clone()));
}
}
fn get_updates_for<'a>(
&self,
uids: &specs::ReadStorage<'a, Uid>,
storage: &specs::ReadStorage<'a, C>,
entity_filter: impl Join + Copy,
buf: &mut Vec<(u64, CompUpdateKind<P>)>,
) {
// Generate inserted updates
for (uid, comp, _, _) in (uids, storage, &self.inserted, entity_filter).join() {
buf.push((
(*uid).into(),
CompUpdateKind::Inserted(P::from(comp.clone())),
));
}
// Generate modified updates
for (uid, comp, _, _) in (uids, storage, &self.modified, entity_filter).join() {
buf.push((
(*uid).into(),
CompUpdateKind::Modified(P::from(comp.clone())),
));
}
// Generate removed updates
for (uid, _, _) in (uids, &self.removed, entity_filter).join() {
buf.push((
(*uid).into(),
CompUpdateKind::Removed(P::Phantom::from(PhantomData::<C>)),
));
}
}
}

88
common/src/sync/uid.rs Normal file
View File

@ -0,0 +1,88 @@
use serde_derive::{Deserialize, Serialize};
use specs::{
saveload::{Marker, MarkerAllocator},
world::EntitiesRes,
Component, Entity, FlaggedStorage, Join, ReadStorage, VecStorage,
};
use std::{collections::HashMap, fmt, u64};
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct Uid(pub u64);
impl Into<u64> for Uid {
fn into(self) -> u64 {
self.0
}
}
impl From<u64> for Uid {
fn from(uid: u64) -> Self {
Self(uid)
}
}
impl fmt::Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Component for Uid {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
impl Marker for Uid {
type Identifier = u64;
type Allocator = UidAllocator;
fn id(&self) -> u64 {
self.0
}
fn update(&mut self, update: Self) {
assert_eq!(self.0, update.0);
}
}
pub struct UidAllocator {
index: u64,
mapping: HashMap<u64, Entity>,
}
impl UidAllocator {
pub fn new() -> Self {
Self {
index: 0,
mapping: HashMap::new(),
}
}
}
impl Default for UidAllocator {
fn default() -> Self {
Self::new()
}
}
impl MarkerAllocator<Uid> for UidAllocator {
fn allocate(&mut self, entity: Entity, id: Option<u64>) -> Uid {
let id = id.unwrap_or_else(|| {
let id = self.index;
self.index += 1;
self.mapping.insert(id, entity);
id
});
Uid(id)
}
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
self.mapping.get(&id).cloned()
}
fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<Uid>) {
self.mapping = (entities, storage)
.join()
.map(|(e, m)| (m.id(), e))
.collect();
}
}

View File

@ -4,7 +4,8 @@ use crate::{
Ori, Pos, Stats,
},
event::{EventBus, LocalEvent, ServerEvent},
state::{DeltaTime, Uid},
state::DeltaTime,
sync::Uid,
};
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration;

View File

@ -7,12 +7,12 @@ use crate::{
},
event::{Emitter, EventBus, LocalEvent, ServerEvent},
state::DeltaTime,
sync::{Uid, UidAllocator},
};
use specs::{
saveload::{Marker, MarkerAllocator},
Entities, Entity, Join, Read, ReadStorage, System, WriteStorage,
};
use sphynx::{Uid, UidAllocator};
use std::time::Duration;
use vek::*;

View File

@ -1,9 +1,11 @@
use crate::comp::{Controller, MountState, Mounting, Ori, Pos, Vel};
use crate::{
comp::{Controller, MountState, Mounting, Ori, Pos, Vel},
sync::UidAllocator,
};
use specs::{
saveload::{Marker, MarkerAllocator},
Entities, Join, Read, System, WriteStorage,
};
use sphynx::UidAllocator;
use vek::*;
/// This system is responsible for controlling mounts

View File

@ -5,10 +5,10 @@ use crate::{
},
event::{EventBus, ServerEvent},
state::DeltaTime,
sync::Uid,
terrain::TerrainGrid,
};
use specs::prelude::*;
use sphynx::Uid;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use std::time::Duration;
use vek::*;

View File

@ -3,11 +3,11 @@ use {
comp::{Body, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Scale, Sticky, Vel},
event::{EventBus, ServerEvent},
state::DeltaTime,
sync::Uid,
terrain::{Block, TerrainGrid},
vol::ReadVol,
},
specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage},
sphynx::Uid,
vek::*,
};

View File

@ -10,8 +10,8 @@ use common::{
msg::ServerMsg,
npc::{get_npc_name, NpcKind},
pathfinding::WorldPath,
sphynx::WorldSyncExt,
state::TimeOfDay,
sync::WorldSyncExt,
terrain::{Block, BlockKind, TerrainChunkSize},
vol::RectVolSize,
};

View File

@ -27,8 +27,8 @@ use common::{
event::{EventBus, ServerEvent},
msg::{ClientMsg, ClientState, ServerError, ServerInfo, ServerMsg},
net::PostOffice,
sphynx::WorldSyncExt,
state::{BlockChange, State, TimeOfDay, Uid},
state::{BlockChange, State, TimeOfDay},
sync::{Uid, WorldSyncExt},
terrain::{block::Block, TerrainChunkSize, TerrainGrid},
vol::{ReadVol, RectVolSize, Vox},
};
@ -899,7 +899,7 @@ impl Server {
.set((before_tick_6 - before_handle_events).as_nanos() as i64);
self.metrics
.tick_time
.with_label_values(&["sphynx sync"])
.with_label_values(&["entity deletion"])
.set((before_tick_7 - before_tick_6).as_nanos() as i64);
self.metrics
.tick_time

View File

@ -10,7 +10,7 @@ use common::{
comp::{CharacterState, ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel},
msg::ServerMsg,
region::{Event as RegionEvent, RegionMap},
state::Uid,
sync::Uid,
};
use specs::{
Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage,
@ -77,8 +77,8 @@ impl<'a> System<'a> for Sys {
// 2. Iterate through region subscribers (ie clients)
// - Collect a list of entity ids for clients who are subscribed to this region (hash calc to check each)
// 3. Iterate through events from that region
// - For each entity left event, iterate through the client list and check if they are subscribed to the destination (hash calc per subscribed client per entity left event)
// - Do something with entity entered events when sphynx is removed??
// - For each entity entered event, iterate through the client list and check if they are subscribed to the source (hash calc per subscribed client per entity event), if not subscribed to the source send a entity creation message to that client
// - For each entity left event, iterate through the client list and check if they are subscribed to the destination (hash calc per subscribed client per entity event)
// 4. Iterate through entities in that region
// 5. Inform clients of the component changes for that entity
// - Throttle update rate base on distance to each client

View File

@ -5,10 +5,11 @@ use common::{
Projectile, Scale, Stats, Sticky,
},
msg::{EcsCompPacket, EcsResPacket},
sphynx::{
self, EntityPackage, ResSyncPackage, StatePackage, SyncPackage, UpdateTracker, WorldSyncExt,
state::{Time, TimeOfDay},
sync::{
CompPacket, EntityPackage, ResSyncPackage, StatePackage, SyncPackage, Uid, UpdateTracker,
WorldSyncExt,
},
state::{Time, TimeOfDay, Uid},
};
use shred_derive::SystemData;
use specs::{
@ -301,7 +302,7 @@ impl<'a> TrackedResources<'a> {
.with_res(self.time_of_day.deref())
}
/// Create state package with resources included
pub fn state_package<C: sphynx::CompPacket>(&self) -> StatePackage<C, EcsResPacket> {
pub fn state_package<C: CompPacket>(&self) -> StatePackage<C, EcsResPacket> {
StatePackage::new()
.with_res(self.time.deref())
.with_res(self.time_of_day.deref())

View File

@ -4,7 +4,7 @@ use common::{
comp::{CharacterState, Ori, Player, Pos, Vel},
msg::ServerMsg,
region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap},
state::Uid,
sync::Uid,
terrain::TerrainChunkSize,
vol::RectVolSize,
};