Add sync_me parameter to Presence that must be set to true for

entities with the `Presence` component to be synced to other clients.
This commit is contained in:
Imbris
2023-06-03 22:23:42 -04:00
parent c6e9d3a202
commit a01f75b38d
5 changed files with 45 additions and 12 deletions

View File

@ -13,16 +13,23 @@ pub struct Presence {
/// updated! /// updated!
pub kind: PresenceKind, pub kind: PresenceKind,
pub lossy_terrain_compression: bool, 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 { 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(); let now = Instant::now();
Self { Self {
terrain_view_distance: ViewDistance::new(view_distances.terrain, now), terrain_view_distance: ViewDistance::new(view_distances.terrain, now),
entity_view_distance: ViewDistance::new(view_distances.entity, now), entity_view_distance: ViewDistance::new(view_distances.entity, now),
kind, kind,
lossy_terrain_compression: false, lossy_terrain_compression: false,
sync_me,
} }
} }
} }

View File

@ -1,4 +1,4 @@
use crate::comp::{Pos, Vel}; use crate::comp::{Pos, Presence, Vel};
use common_base::span; use common_base::span;
use hashbrown::{hash_map::DefaultHashBuilder, HashSet}; use hashbrown::{hash_map::DefaultHashBuilder, HashSet};
use indexmap::IndexMap; use indexmap::IndexMap;
@ -69,7 +69,12 @@ const NEIGHBOR_OFFSETS: [Vec2<i32>; 8] = [
#[derive(Default)] #[derive(Default)]
// TODO generic region size (16x16 for now) // TODO generic region size (16x16 for now)
// TODO compare to sweep and prune approach // 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 { pub struct RegionMap {
// Tree? // Tree?
// Sorted Vec? (binary search lookup) // Sorted Vec? (binary search lookup)
@ -92,7 +97,13 @@ impl RegionMap {
// TODO maintain within a system? // TODO maintain within a system?
// TODO special case large entities // TODO special case large entities
pub fn tick(&mut self, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>, entities: Entities) { pub fn tick(
&mut self,
pos: ReadStorage<Pos>,
vel: ReadStorage<Vel>,
presence: ReadStorage<Presence>,
entities: Entities,
) {
span!(_guard, "tick", "Region::tick"); span!(_guard, "tick", "Region::tick");
self.tick += 1; self.tick += 1;
// Clear events within each region // Clear events within each region
@ -101,9 +112,10 @@ impl RegionMap {
}); });
// Add any untracked entities // 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() .join()
.map(|(pos, e, _)| (pos, e.id())) .filter(|(_, _, presence, _)| presence.map_or(true, |p| p.sync_me))
.map(|(pos, e, _, _)| (pos, e.id()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
{ {
// Add entity // Add entity
@ -123,15 +135,21 @@ impl RegionMap {
.iter() .iter()
.enumerate() .enumerate()
.for_each(|(i, (&current_region, region_data))| { .for_each(|(i, (&current_region, region_data))| {
for (maybe_pos, _maybe_vel, id) in for (maybe_pos, _maybe_vel, maybe_presence, id) in (
(pos.maybe(), vel.maybe(), &region_data.bitset).join() pos.maybe(),
vel.maybe(),
presence.maybe(),
&region_data.bitset,
)
.join()
{ {
let should_sync = maybe_presence.map_or(true, |p| p.sync_me);
match maybe_pos { match maybe_pos {
// Switch regions for entities which need switching // Switch regions for entities which need switching
// TODO don't check every tick (use velocity) (and use id to stagger) // TODO don't check every tick (use velocity) (and use id to stagger)
// Starting parameters at v = 0 check every 100 ticks // Starting parameters at v = 0 check every 100 ticks
// tether_length^2 / vel^2 (with a max of every tick) // 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 pos = pos.0.map(|e| e as i32);
let key = Self::pos_key(pos); let key = Self::pos_key(pos);
// Consider switching // Consider switching
@ -148,7 +166,7 @@ impl RegionMap {
}, },
// Remove any non-existant entities (or just ones that lost their position // Remove any non-existant entities (or just ones that lost their position
// component) TODO: distribute this between ticks // component) TODO: distribute this between ticks
None => { None | Some(_) => {
// TODO: shouldn't there be a way to extract the bitset of entities with // TODO: shouldn't there be a way to extract the bitset of entities with
// positions directly from specs? Yes, with `.mask()` on the component // positions directly from specs? Yes, with `.mask()` on the component
// storage. // storage.

View File

@ -534,6 +534,7 @@ impl State {
self.ecs.write_resource::<RegionMap>().tick( self.ecs.write_resource::<RegionMap>().tick(
self.ecs.read_storage::<comp::Pos>(), self.ecs.read_storage::<comp::Pos>(),
self.ecs.read_storage::<comp::Vel>(), self.ecs.read_storage::<comp::Vel>(),
self.ecs.read_storage::<comp::Presence>(),
self.ecs.entities(), self.ecs.entities(),
); );
} }

View File

@ -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. // from overwriting original character info with stuff from the new character.
kind: PresenceKind::Possessor, kind: PresenceKind::Possessor,
lossy_terrain_compression: presence.lossy_terrain_compression, lossy_terrain_compression: presence.lossy_terrain_compression,
sync_me: presence.sync_me,
}) })
} else { } else {
None None

View File

@ -604,6 +604,7 @@ impl StateExt for State {
entity: view_distance, entity: view_distance,
}, },
PresenceKind::Spectator, PresenceKind::Spectator,
false,
)) ))
} }
@ -644,7 +645,11 @@ impl StateExt for State {
self.write_component_ignore_entity_dead( self.write_component_ignore_entity_dead(
entity, 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. // Tell the client its request was successful.
@ -669,7 +674,7 @@ impl StateExt for State {
self.write_component_ignore_entity_dead( self.write_component_ignore_entity_dead(
entity, entity,
Presence::new(view_distances, PresenceKind::Spectator), Presence::new(view_distances, PresenceKind::Spectator, false),
); );
// Tell the client its request was successful. // Tell the client its request was successful.
@ -704,6 +709,7 @@ impl StateExt for State {
self.ecs() self.ecs()
.write_resource::<IdMaps>() .write_resource::<IdMaps>()
.add_character(id, entity); .add_character(id, entity);
presence.sync_me = true;
Ok(()) Ok(())
} else { } else {
Err("PresenceKind is not LoadingCharacter") Err("PresenceKind is not LoadingCharacter")