diff --git a/common/src/comp/presence.rs b/common/src/comp/presence.rs index 7148d220a7..d08cba3cf6 100644 --- a/common/src/comp/presence.rs +++ b/common/src/comp/presence.rs @@ -13,16 +13,23 @@ pub struct Presence { /// updated! pub kind: PresenceKind, pub lossy_terrain_compression: bool, + /// Controls whether this entity is synced to other clients. + /// + /// Note, if it ends up being useful this could be generalized to an + /// independent component that is required for any entity to be synced + /// (as an independent component it could use NullStorage). + pub sync_me: bool, } impl Presence { - pub fn new(view_distances: ViewDistances, kind: PresenceKind) -> Self { + pub fn new(view_distances: ViewDistances, kind: PresenceKind, sync_me: bool) -> Self { let now = Instant::now(); Self { terrain_view_distance: ViewDistance::new(view_distances.terrain, now), entity_view_distance: ViewDistance::new(view_distances.entity, now), kind, lossy_terrain_compression: false, + sync_me, } } } diff --git a/common/src/region.rs b/common/src/region.rs index d01b026268..cd34173684 100644 --- a/common/src/region.rs +++ b/common/src/region.rs @@ -1,4 +1,4 @@ -use crate::comp::{Pos, Vel}; +use crate::comp::{Pos, Presence, Vel}; use common_base::span; use hashbrown::{hash_map::DefaultHashBuilder, HashSet}; use indexmap::IndexMap; @@ -69,7 +69,12 @@ const NEIGHBOR_OFFSETS: [Vec2; 8] = [ #[derive(Default)] // TODO generic region size (16x16 for now) // TODO compare to sweep and prune approach -/// A region system that tracks where entities are +/// A region system that tracks where entities are. +/// +/// Note, this structure is primarily intended for tracking which entities need +/// to be synchronized to which clients (and as part of that what entities are +/// already synchronized). If an entity is marked to not be synchronized to +/// other clients it may not appear here. pub struct RegionMap { // Tree? // Sorted Vec? (binary search lookup) @@ -92,7 +97,13 @@ impl RegionMap { // TODO maintain within a system? // TODO special case large entities - pub fn tick(&mut self, pos: ReadStorage, vel: ReadStorage, entities: Entities) { + pub fn tick( + &mut self, + pos: ReadStorage, + vel: ReadStorage, + presence: ReadStorage, + entities: Entities, + ) { span!(_guard, "tick", "Region::tick"); self.tick += 1; // Clear events within each region @@ -101,9 +112,10 @@ impl RegionMap { }); // Add any untracked entities - for (pos, id) in (&pos, &entities, !&self.tracked_entities) + for (pos, id) in (&pos, &entities, presence.maybe(), !&self.tracked_entities) .join() - .map(|(pos, e, _)| (pos, e.id())) + .filter(|(_, _, presence, _)| presence.map_or(true, |p| p.sync_me)) + .map(|(pos, e, _, _)| (pos, e.id())) .collect::>() { // Add entity @@ -123,15 +135,21 @@ impl RegionMap { .iter() .enumerate() .for_each(|(i, (¤t_region, region_data))| { - for (maybe_pos, _maybe_vel, id) in - (pos.maybe(), vel.maybe(), ®ion_data.bitset).join() + for (maybe_pos, _maybe_vel, maybe_presence, id) in ( + pos.maybe(), + vel.maybe(), + presence.maybe(), + ®ion_data.bitset, + ) + .join() { + let should_sync = maybe_presence.map_or(true, |p| p.sync_me); match maybe_pos { // Switch regions for entities which need switching // TODO don't check every tick (use velocity) (and use id to stagger) // Starting parameters at v = 0 check every 100 ticks // tether_length^2 / vel^2 (with a max of every tick) - Some(pos) => { + Some(pos) if should_sync => { let pos = pos.0.map(|e| e as i32); let key = Self::pos_key(pos); // Consider switching @@ -148,7 +166,7 @@ impl RegionMap { }, // Remove any non-existant entities (or just ones that lost their position // component) TODO: distribute this between ticks - None => { + None | Some(_) => { // TODO: shouldn't there be a way to extract the bitset of entities with // positions directly from specs? Yes, with `.mask()` on the component // storage. diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 7771d54f00..6c1ee98404 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -534,6 +534,7 @@ impl State { self.ecs.write_resource::().tick( self.ecs.read_storage::(), self.ecs.read_storage::(), + self.ecs.read_storage::(), self.ecs.entities(), ); } diff --git a/server/src/events/player.rs b/server/src/events/player.rs index dff4d34704..f329c3bada 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -418,6 +418,7 @@ pub fn handle_possess(server: &mut Server, possessor_uid: Uid, possessee_uid: Ui // from overwriting original character info with stuff from the new character. kind: PresenceKind::Possessor, lossy_terrain_compression: presence.lossy_terrain_compression, + sync_me: presence.sync_me, }) } else { None diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index ae77bd5d53..eedded76f5 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -604,6 +604,7 @@ impl StateExt for State { entity: view_distance, }, PresenceKind::Spectator, + false, )) } @@ -644,7 +645,11 @@ impl StateExt for State { self.write_component_ignore_entity_dead( entity, - Presence::new(view_distances, PresenceKind::LoadingCharacter(character_id)), + Presence::new( + view_distances, + PresenceKind::LoadingCharacter(character_id), + false, + ), ); // Tell the client its request was successful. @@ -669,7 +674,7 @@ impl StateExt for State { self.write_component_ignore_entity_dead( entity, - Presence::new(view_distances, PresenceKind::Spectator), + Presence::new(view_distances, PresenceKind::Spectator, false), ); // Tell the client its request was successful. @@ -704,6 +709,7 @@ impl StateExt for State { self.ecs() .write_resource::() .add_character(id, entity); + presence.sync_me = true; Ok(()) } else { Err("PresenceKind is not LoadingCharacter")