Delete Vel and Ori on the client when they are removed on the server

This commit is contained in:
Imbris 2020-03-18 17:00:07 -04:00
parent 038f655269
commit 1a484410ca
10 changed files with 219 additions and 228 deletions

View File

@ -629,8 +629,15 @@ impl Client {
ServerMsg::TimeOfDay(time_of_day) => {
*self.state.ecs_mut().write_resource() = time_of_day;
},
ServerMsg::EcsSync(sync_package) => {
self.state.ecs_mut().apply_sync_package(sync_package);
ServerMsg::EntitySync(entity_sync_package) => {
self.state
.ecs_mut()
.apply_entity_sync_package(entity_sync_package);
},
ServerMsg::CompSync(comp_sync_package) => {
self.state
.ecs_mut()
.apply_comp_sync_package(comp_sync_package);
},
ServerMsg::CreateEntity(entity_package) => {
self.state.ecs_mut().apply_entity_package(entity_package);
@ -667,29 +674,6 @@ impl Client {
.allocate(entity_builder.entity, Some(client_uid));
self.entity = entity_builder.with(uid).build();
},
ServerMsg::EntityPos { entity, pos } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, pos);
}
},
ServerMsg::EntityVel { entity, vel } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, vel);
}
},
ServerMsg::EntityOri { entity, ori } => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, ori);
}
},
ServerMsg::EntityCharacterState {
entity,
character_state,
} => {
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
self.state.write_component(entity, character_state);
}
},
ServerMsg::InventoryUpdate(inventory, event) => {
match event {
InventoryUpdateEvent::CollectFailed => {

View File

@ -25,6 +25,9 @@ sum_type! {
Loadout(comp::Loadout),
Attacking(comp::Attacking),
CharacterState(comp::CharacterState),
Pos(comp::Pos),
Vel(comp::Vel),
Ori(comp::Ori),
}
}
// Automatically derive From<T> for EcsCompPhantom
@ -49,6 +52,9 @@ sum_type! {
Loadout(PhantomData<comp::Loadout>),
Attacking(PhantomData<comp::Attacking>),
CharacterState(PhantomData<comp::CharacterState>),
Pos(PhantomData<comp::Pos>),
Vel(PhantomData<comp::Vel>),
Ori(PhantomData<comp::Ori>),
}
}
impl sync::CompPacket for EcsCompPacket {
@ -73,6 +79,9 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Loadout(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::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),
}
}
@ -95,6 +104,9 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Loadout(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::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),
}
}
@ -123,6 +135,9 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPhantom::CharacterState(_) => {
sync::handle_remove::<comp::CharacterState>(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),
}
}
}

View File

@ -46,25 +46,10 @@ pub enum ServerMsg {
},
SetPlayerEntity(u64),
TimeOfDay(state::TimeOfDay),
EcsSync(sync::SyncPackage<EcsCompPacket>),
EntitySync(sync::EntitySyncPackage),
CompSync(sync::CompSyncPackage<EcsCompPacket>),
CreateEntity(sync::EntityPackage<EcsCompPacket>),
DeleteEntity(u64),
EntityPos {
entity: u64,
pos: comp::Pos,
},
EntityVel {
entity: u64,
vel: comp::Vel,
},
EntityOri {
entity: u64,
ori: comp::Ori,
},
EntityCharacterState {
entity: u64,
character_state: comp::CharacterState,
},
InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent),
TerrainChunkUpdate {
key: Vec2<i32>,

View File

@ -7,8 +7,8 @@ mod uid;
// Reexports
pub use packet::{
handle_insert, handle_modify, handle_remove, CompPacket, EntityPackage, StatePackage,
SyncPackage,
handle_insert, handle_modify, handle_remove, CompPacket, CompSyncPackage, EntityPackage,
EntitySyncPackage, StatePackage,
};
pub use sync_ext::WorldSyncExt;
pub use track::UpdateTracker;

View File

@ -23,18 +23,22 @@ pub trait CompPacket: Clone + Debug + Send + 'static {
pub fn handle_insert<C: Component>(comp: C, entity: Entity, world: &World) {
if let Err(err) = world.write_storage::<C>().insert(entity, comp) {
error!("Error inserting component: {:?}", err);
};
}
}
/// Useful for implementing CompPacket trait
pub fn handle_modify<C: Component>(comp: C, entity: Entity, world: &World) {
let _ = world
if world
.write_storage::<C>()
.get_mut(entity)
.map(|c| *c = comp);
.map(|c| *c = comp)
.is_none()
{
error!("Error modifying synced component, it doesn't seem to exist");
}
}
/// Useful for implementing CompPacket trait
pub fn handle_remove<C: Component>(entity: Entity, world: &World) {
let _ = world.write_storage::<C>().remove(entity);
world.write_storage::<C>().remove(entity);
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
@ -81,12 +85,11 @@ impl<P: CompPacket> StatePackage<P> {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SyncPackage<P: CompPacket> {
pub comp_updates: Vec<(u64, CompUpdateKind<P>)>,
pub struct EntitySyncPackage {
pub created_entities: Vec<u64>,
pub deleted_entities: Vec<u64>,
}
impl<P: CompPacket> SyncPackage<P> {
impl EntitySyncPackage {
pub fn new<'a>(
uids: &ReadStorage<'a, Uid>,
uid_tracker: &UpdateTracker<Uid>,
@ -100,11 +103,48 @@ impl<P: CompPacket> SyncPackage<P> {
.collect();
Self {
comp_updates: Vec::new(),
created_entities,
deleted_entities,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CompSyncPackage<P: CompPacket> {
// TODO: this can be made to take less space by clumping updates for the same entity together
pub comp_updates: Vec<(u64, CompUpdateKind<P>)>,
}
impl<P: CompPacket> CompSyncPackage<P> {
pub fn new() -> Self {
Self {
comp_updates: Vec::new(),
}
}
pub fn comp_inserted<C>(&mut self, uid: Uid, comp: C)
where
P: From<C>,
{
self.comp_updates
.push((uid.into(), CompUpdateKind::Inserted(comp.into())));
}
pub fn comp_modified<C>(&mut self, uid: Uid, comp: C)
where
P: From<C>,
{
self.comp_updates
.push((uid.into(), CompUpdateKind::Modified(comp.into())));
}
pub fn comp_removed<C>(&mut self, uid: Uid)
where
P::Phantom: From<PhantomData<C>>,
{
self.comp_updates
.push((uid.into(), CompUpdateKind::Removed(PhantomData::<C>.into())));
}
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
mut self,

View File

@ -1,5 +1,7 @@
use super::{
packet::{CompPacket, CompUpdateKind, EntityPackage, StatePackage, SyncPackage},
packet::{
CompPacket, CompSyncPackage, CompUpdateKind, EntityPackage, EntitySyncPackage, StatePackage,
},
track::UpdateTracker,
uid::{Uid, UidAllocator},
};
@ -27,7 +29,8 @@ pub trait WorldSyncExt {
entity_package: EntityPackage<P>,
) -> specs::Entity;
fn apply_state_package<P: CompPacket>(&mut self, state_package: StatePackage<P>);
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>);
fn apply_entity_sync_package(&mut self, package: EntitySyncPackage);
fn apply_comp_sync_package<P: CompPacket>(&mut self, package: CompSyncPackage<P>);
}
impl WorldSyncExt for specs::World {
@ -106,24 +109,30 @@ impl WorldSyncExt for specs::World {
//self.maintain();
}
fn apply_sync_package<P: CompPacket>(&mut self, package: SyncPackage<P>) {
fn apply_entity_sync_package(&mut self, package: EntitySyncPackage) {
// Take ownership of the fields
let SyncPackage {
comp_updates,
let EntitySyncPackage {
created_entities,
deleted_entities,
} = package;
// Attempt to create entities
for entity_uid in created_entities {
create_entity_with_uid(self, entity_uid);
}
created_entities.into_iter().for_each(|uid| {
create_entity_with_uid(self, uid);
});
// Attempt to delete entities that were marked for deletion
deleted_entities.into_iter().for_each(|uid| {
self.delete_entity_and_clear_from_uid_allocator(uid);
});
}
fn apply_comp_sync_package<P: CompPacket>(&mut self, package: CompSyncPackage<P>) {
// Update components
for (entity_uid, update) in comp_updates {
package.comp_updates.into_iter().for_each(|(uid, update)| {
if let Some(entity) = self
.read_resource::<UidAllocator>()
.retrieve_entity_internal(entity_uid)
.retrieve_entity_internal(uid)
{
match update {
CompUpdateKind::Inserted(packet) => {
@ -137,12 +146,7 @@ impl WorldSyncExt for specs::World {
},
}
}
}
// Attempt to delete entities that were marked for deletion
for entity_uid in deleted_entities {
self.delete_entity_and_clear_from_uid_allocator(entity_uid);
}
});
}
}

View File

@ -468,7 +468,7 @@ impl Server {
.notify(ServerMsg::InitialSync {
// Send client their entity
entity_package: TrackedComps::fetch(&self.state.ecs())
.create_entity_package(entity),
.create_entity_package(entity, None, None, None),
server_info: self.server_info.clone(),
time_of_day: *self.state.ecs().read_resource(),
world_map: (WORLD_SIZE.map(|e| e as u32), self.map.clone()),

View File

@ -11,7 +11,7 @@ use common::{
msg::ServerMsg,
region::{Event as RegionEvent, RegionMap},
state::TimeOfDay,
sync::Uid,
sync::{CompSyncPackage, Uid},
};
use specs::{
Entities, Entity as EcsEntity, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage,
@ -30,13 +30,11 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Pos>,
ReadStorage<'a, Vel>,
ReadStorage<'a, Ori>,
ReadStorage<'a, CharacterState>,
ReadStorage<'a, Inventory>,
ReadStorage<'a, RegionSubscription>,
WriteStorage<'a, Last<Pos>>,
WriteStorage<'a, Last<Vel>>,
WriteStorage<'a, Last<Ori>>,
WriteStorage<'a, Last<CharacterState>>,
WriteStorage<'a, Client>,
WriteStorage<'a, ForceUpdate>,
WriteStorage<'a, InventoryUpdate>,
@ -57,13 +55,11 @@ impl<'a> System<'a> for Sys {
positions,
velocities,
orientations,
character_states,
inventories,
subscriptions,
mut last_pos,
mut last_vel,
mut last_ori,
mut last_character_state,
mut clients,
mut force_updates,
mut inventory_updates,
@ -116,22 +112,18 @@ impl<'a> System<'a> for Sys {
continue;
}
let entity = entities.entity(*id);
if let Some((uid, pos, vel, ori, character_state)) =
uids.get(entity).and_then(|uid| {
positions.get(entity).map(|pos| {
(
uid,
pos,
velocities.get(entity),
orientations.get(entity),
character_states.get(entity),
)
})
if let Some((uid, pos, vel, ori)) = uids.get(entity).and_then(|uid| {
positions.get(entity).map(|pos| {
(uid, pos, velocities.get(entity), orientations.get(entity))
})
{
let create_msg = ServerMsg::CreateEntity(
tracked_comps.create_entity_package(entity),
);
}) {
let create_msg =
ServerMsg::CreateEntity(tracked_comps.create_entity_package(
entity,
Some(*pos),
vel.copied(),
ori.copied(),
));
for (client, regions, client_entity, _) in &mut subscribers {
if maybe_key
.as_ref()
@ -141,14 +133,6 @@ impl<'a> System<'a> for Sys {
&& *client_entity != entity
{
client.notify(create_msg.clone());
send_initial_unsynced_components(
client,
uid,
pos,
vel,
ori,
character_state,
);
}
}
}
@ -172,18 +156,19 @@ impl<'a> System<'a> for Sys {
// Sync tracked components
// Get deleted entities in this region from DeletedEntities
let sync_msg = ServerMsg::EcsSync(
trackers.create_sync_package(
&tracked_comps,
region.entities(),
deleted_entities
.take_deleted_in_region(key)
.unwrap_or_else(|| Vec::new()),
),
let (entity_sync_package, comp_sync_package) = trackers.create_sync_packages(
&tracked_comps,
region.entities(),
deleted_entities
.take_deleted_in_region(key)
.unwrap_or_else(|| Vec::new()),
);
for (client, _, _, _) in &mut subscribers {
client.notify(sync_msg.clone());
}
let entity_sync_msg = ServerMsg::EntitySync(entity_sync_package);
let comp_sync_msg = ServerMsg::CompSync(comp_sync_package);
subscribers.iter_mut().for_each(move |(client, _, _, _)| {
client.notify(entity_sync_msg.clone());
client.notify(comp_sync_msg.clone());
});
let mut send_msg = |msg: ServerMsg,
entity: EcsEntity,
@ -191,117 +176,112 @@ impl<'a> System<'a> for Sys {
force_update: Option<&ForceUpdate>,
throttle: bool| {
for (client, _, client_entity, client_pos) in &mut subscribers {
let update = if client_entity == &entity {
if if client_entity == &entity {
// Don't send client physics updates about itself unless force update is set
force_update.is_some()
} else if !throttle {
// Update rate not thottled by distance
// Send the message if not throttling
true
} else {
// Throttle update rate based on distance to client
let distance_sq = client_pos.0.distance_squared(pos.0);
let id_staggered_tick = tick + entity.id() as u64;
// More entities farther away so checks start there
if distance_sq > 300.0f32.powi(2) {
(tick + entity.id() as u64) % 32 == 0
id_staggered_tick % 32 == 0
} else if distance_sq > 250.0f32.powi(2) {
(tick + entity.id() as u64) % 16 == 0
id_staggered_tick % 16 == 0
} else if distance_sq > 200.0f32.powi(2) {
(tick + entity.id() as u64) % 8 == 0
id_staggered_tick % 8 == 0
} else if distance_sq > 150.0f32.powi(2) {
(tick + entity.id() as u64) % 4 == 0
id_staggered_tick % 4 == 0
} else if distance_sq > 100.0f32.powi(2) {
(tick + entity.id() as u64) % 2 == 0
id_staggered_tick % 2 == 0
} else {
true // Closer than 100 blocks
}
};
if update {
} {
client.notify(msg.clone());
}
}
};
// Sync physics components
for (_, entity, &uid, &pos, maybe_vel, maybe_ori, character_state, force_update) in (
for (_, entity, &uid, &pos, maybe_vel, maybe_ori, force_update) in (
region.entities(),
&entities,
&uids,
&positions,
velocities.maybe(),
orientations.maybe(),
character_states.maybe(),
force_updates.maybe(),
)
.join()
{
let mut comp_sync_package = CompSyncPackage::new();
let mut throttle = true;
// TODO: An entity that stoppped moving on a tick that it wasn't sent to the
// player will never have it's position updated
if last_pos.get(entity).map(|&l| l.0 != pos).unwrap_or(true) {
let _ = last_pos.insert(entity, Last(pos));
send_msg(
ServerMsg::EntityPos {
entity: uid.into(),
pos,
},
entity,
pos,
force_update,
true,
);
match last_pos.get(entity).map(|&l| l.0 != pos) {
Some(false) => {},
Some(true) => {
let _ = last_pos.insert(entity, Last(pos));
comp_sync_package.comp_modified(uid, pos);
},
None => {
let _ = last_pos.insert(entity, Last(pos));
throttle = false;
comp_sync_package.comp_inserted(uid, pos);
},
}
if let Some(&vel) = maybe_vel {
if last_vel.get(entity).map(|&l| l.0 != vel).unwrap_or(true) {
let _ = last_vel.insert(entity, Last(vel));
send_msg(
ServerMsg::EntityVel {
entity: uid.into(),
vel,
},
entity,
pos,
force_update,
true,
);
match last_vel.get(entity).map(|&l| l.0 != vel) {
Some(false) => {},
Some(true) => {
let _ = last_vel.insert(entity, Last(vel));
comp_sync_package.comp_modified(uid, vel);
},
None => {
let _ = last_vel.insert(entity, Last(vel));
throttle = false;
comp_sync_package.comp_inserted(uid, vel);
},
}
} else if last_vel.remove(entity).is_some() {
// Send removal message if Vel was removed
// Note: we don't have to handle this for position because the entity will be
// removed from the client by the region system
throttle = false;
comp_sync_package.comp_removed::<Vel>(uid);
}
if let Some(&ori) = maybe_ori {
if last_ori.get(entity).map(|&l| l.0 != ori).unwrap_or(true) {
let _ = last_ori.insert(entity, Last(ori));
send_msg(
ServerMsg::EntityOri {
entity: uid.into(),
ori,
},
entity,
pos,
force_update,
true,
);
match last_ori.get(entity).map(|&l| l.0 != ori) {
Some(false) => {},
Some(true) => {
let _ = last_ori.insert(entity, Last(ori));
comp_sync_package.comp_modified(uid, ori);
},
None => {
let _ = last_ori.insert(entity, Last(ori));
throttle = false;
comp_sync_package.comp_inserted(uid, ori);
},
}
} else if last_ori.remove(entity).is_some() {
// Send removal message if Ori was removed
throttle = false;
comp_sync_package.comp_removed::<Ori>(uid);
}
if let Some(&character_state) = character_state.as_ref() {
if last_character_state
.get(entity)
.map(|l| !character_state.equals(&l.0))
.unwrap_or(true)
{
let _ = last_character_state.insert(entity, Last(character_state.clone()));
send_msg(
ServerMsg::EntityCharacterState {
entity: uid.into(),
character_state: character_state.clone(),
},
entity,
pos,
force_update,
false,
);
}
}
send_msg(
ServerMsg::CompSync(comp_sync_package),
entity,
pos,
force_update,
throttle,
);
}
}
@ -350,27 +330,3 @@ impl<'a> System<'a> for Sys {
timer.end();
}
}
pub fn send_initial_unsynced_components(
client: &mut Client,
uid: &Uid,
pos: &Pos,
vel: Option<&Vel>,
ori: Option<&Ori>,
character_state: Option<&CharacterState>,
) {
let entity = (*uid).into();
client.notify(ServerMsg::EntityPos { entity, pos: *pos });
if let Some(&vel) = vel {
client.notify(ServerMsg::EntityVel { entity, vel });
}
if let Some(&ori) = ori {
client.notify(ServerMsg::EntityOri { entity, ori });
}
if let Some(character_state) = character_state.cloned() {
client.notify(ServerMsg::EntityCharacterState {
entity,
character_state,
});
}
}

View File

@ -2,10 +2,10 @@ use super::SysTimer;
use common::{
comp::{
Body, CanBuild, CharacterState, Energy, Gravity, Item, LightEmitter, Loadout, Mass,
MountState, Mounting, Player, Scale, Stats, Sticky,
MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
},
msg::EcsCompPacket,
sync::{EntityPackage, SyncPackage, Uid, UpdateTracker, WorldSyncExt},
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
};
use hashbrown::HashMap;
use specs::{
@ -55,7 +55,13 @@ pub struct TrackedComps<'a> {
pub character_state: ReadStorage<'a, CharacterState>,
}
impl<'a> TrackedComps<'a> {
pub fn create_entity_package(&self, entity: EcsEntity) -> EntityPackage<EcsCompPacket> {
pub fn create_entity_package(
&self,
entity: EcsEntity,
pos: Option<Pos>,
vel: Option<Vel>,
ori: Option<Ori>,
) -> EntityPackage<EcsCompPacket> {
let uid = self
.uid
.get(entity)
@ -114,6 +120,10 @@ impl<'a> TrackedComps<'a> {
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
// Add untracked comps
pos.map(|c| comps.push(c.into()));
vel.map(|c| comps.push(c.into()));
ori.map(|c| comps.push(c.into()));
EntityPackage { uid, comps }
}
@ -138,13 +148,15 @@ pub struct ReadTrackers<'a> {
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
}
impl<'a> ReadTrackers<'a> {
pub fn create_sync_package(
pub fn create_sync_packages(
&self,
comps: &TrackedComps,
filter: impl Join + Copy,
deleted_entities: Vec<u64>,
) -> SyncPackage<EcsCompPacket> {
SyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities)
) -> (EntitySyncPackage, CompSyncPackage<EcsCompPacket>) {
let entity_sync_package =
EntitySyncPackage::new(&comps.uid, &self.uid, filter, deleted_entities);
let comp_sync_package = CompSyncPackage::new()
.with_component(&comps.uid, &*self.body, &comps.body, filter)
.with_component(&comps.uid, &*self.player, &comps.player, filter)
.with_component(&comps.uid, &*self.stats, &comps.stats, filter)
@ -169,7 +181,9 @@ impl<'a> ReadTrackers<'a> {
&*self.character_state,
&comps.character_state,
filter,
)
);
(entity_sync_package, comp_sync_package)
}
}

View File

@ -194,19 +194,16 @@ impl<'a> System<'a> for Sys {
.join()
.filter(|(_, _, _, _, _, _, e)| *e != client_entity)
{
// Send message to create entity and tracked components
// Send message to create entity and tracked components and physics
// components
client.notify(ServerMsg::CreateEntity(
tracked_comps.create_entity_package(entity),
tracked_comps.create_entity_package(
entity,
Some(*pos),
vel.copied(),
ori.copied(),
),
));
// Send message to create physics components
super::entity_sync::send_initial_unsynced_components(
client,
uid,
pos,
vel,
ori,
character_state,
);
}
}
}
@ -253,19 +250,15 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
)
.join()
{
// Send message to create entity and tracked components
// Send message to create entity and tracked components and physics components
client.notify(ServerMsg::CreateEntity(
tracked_comps.create_entity_package(entity),
tracked_comps.create_entity_package(
entity,
Some(*pos),
vel.copied(),
ori.copied(),
),
));
// Send message to create physics components
super::entity_sync::send_initial_unsynced_components(
client,
uid,
pos,
vel,
ori,
character_state,
);
}
}
}