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!
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,
}
}
}

View File

@ -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<i32>; 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<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");
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::<Vec<_>>()
{
// Add entity
@ -123,15 +135,21 @@ impl RegionMap {
.iter()
.enumerate()
.for_each(|(i, (&current_region, region_data))| {
for (maybe_pos, _maybe_vel, id) in
(pos.maybe(), vel.maybe(), &region_data.bitset).join()
for (maybe_pos, _maybe_vel, maybe_presence, id) in (
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 {
// 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.

View File

@ -534,6 +534,7 @@ impl State {
self.ecs.write_resource::<RegionMap>().tick(
self.ecs.read_storage::<comp::Pos>(),
self.ecs.read_storage::<comp::Vel>(),
self.ecs.read_storage::<comp::Presence>(),
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.
kind: PresenceKind::Possessor,
lossy_terrain_compression: presence.lossy_terrain_compression,
sync_me: presence.sync_me,
})
} else {
None

View File

@ -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::<IdMaps>()
.add_character(id, entity);
presence.sync_me = true;
Ok(())
} else {
Err("PresenceKind is not LoadingCharacter")