When sync_me is false avoid:

* Logging a warning when deleting the entity and it is not in any
  Region.
* Searching every single region for an entity that is in none of them.

Also:
* Add workaround for bug in specs where deleting any entity clears the
  components before checking that the generation is correct (surprised
  that we haven't encounted bugs from this yet).
* Properly update `tracked_entities` inside `RegionMap` when deleting
  an entity. Previously, we just relied on this being updated in
  `RegionMap::tick` by the absence of the `Pos` component at that index.
  However, this means if a new entity is created at that index after
  deletion and before calling `RegionMap::tick`, then this can be
  interpreted as an entity moving between regions rather than one being
  deleted and another created. It seems like this could lead to
  synchronization bugs like not creating the new entity on the client
  (although I haven't noticed this before, I think maybe we use newly
  inserted `Uid`s to detect new entities rather than the region system?).
  I think it could at least lead to sending redundant messages to
  synchronize the new entity.
This commit is contained in:
Imbris
2023-06-06 02:25:45 -04:00
parent cdca700297
commit ecb27deeae
5 changed files with 48 additions and 45 deletions

View File

@ -709,7 +709,7 @@ impl StateExt for State {
self.ecs()
.write_resource::<IdMaps>()
.add_character(id, entity);
//presence.sync_me = true;
presence.sync_me = true;
Ok(())
} else {
Err("PresenceKind is not LoadingCharacter")
@ -1196,20 +1196,21 @@ impl StateExt for State {
// NOTE: We expect that these 3 components are never removed from an entity (nor
// mutated) (at least not without updating the relevant mappings)!
let maybe_uid = self.read_component_copied::<Uid>(entity);
let maybe_character = self
let (maybe_character, sync_me) = self
.read_storage::<Presence>()
.get(entity)
.and_then(|p| p.kind.character_id());
.map(|p| (p.kind.character_id(), p.sync_me))
.unzip();
let maybe_rtsim = self.read_component_copied::<RtSimEntity>(entity);
self.mut_resource::<IdMaps>().remove_entity(
Some(entity),
maybe_uid,
maybe_character,
maybe_character.flatten(),
maybe_rtsim,
);
delete_entity_common(self, entity, maybe_uid)
delete_entity_common(self, entity, maybe_uid, sync_me.unwrap_or(true))
}
fn entity_as_actor(&self, entity: EcsEntity) -> Option<Actor> {
@ -1247,6 +1248,7 @@ pub(crate) fn delete_entity_common(
state: &mut State,
entity: EcsEntity,
maybe_uid: Option<Uid>,
sync_me: bool,
) -> Result<(), specs::error::WrongGeneration> {
if maybe_uid.is_none() {
// For now we expect all entities have a Uid component.
@ -1254,8 +1256,24 @@ pub(crate) fn delete_entity_common(
}
let maybe_pos = state.read_component_copied::<comp::Pos>(entity);
let res = state.ecs_mut().delete_entity(entity);
// TODO: workaround for https://github.com/amethyst/specs/pull/766
let actual_gen = state.ecs().entities().entity(entity.id()).gen();
let res = if actual_gen == entity.gen() {
state.ecs_mut().delete_entity(entity)
} else {
Err(specs::error::WrongGeneration {
action: "delete",
actual_gen,
entity,
})
};
if res.is_ok() {
let region_map = state.mut_resource::<common::region::RegionMap>();
let uid_pos_region_key = maybe_uid
.zip(maybe_pos)
.map(|(uid, pos)| (uid, pos, region_map.find_region(entity, pos.0)));
region_map.entity_deleted(entity);
// Note: Adding the `Uid` to the deleted list when exiting "in-game" relies on
// the client not being able to immediately re-enter the game in the
// same tick (since we could then mix up the ordering of things and
@ -1263,16 +1281,14 @@ pub(crate) fn delete_entity_common(
//
// The client will ignore requests to delete its own entity that are triggered
// by this.
if let Some((uid, pos)) = maybe_uid.zip(maybe_pos) {
let region_key = state
.ecs()
.read_resource::<common::region::RegionMap>()
.find_region(entity, pos.0);
if let Some((uid, pos, region_key)) = uid_pos_region_key {
if let Some(region_key) = region_key {
state
.mut_resource::<DeletedEntities>()
.record_deleted_entity(uid, region_key);
} else {
// If there is a position and sync_me is true, but the entity is not
// in a region, something might be wrong.
} else if sync_me {
// Don't panic if the entity wasn't found in a region, maybe it was just created
// and then deleted before the region manager had a chance to assign it a region
warn!(