From 89e0925c325d6ed4080150df768bd3ea905a6e95 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 28 Jul 2020 00:11:07 +1000 Subject: [PATCH] Better AI reachable cleanup (#1507) Haven't profiled so can't say if it definitely fixes the leak. Co-authored-by: Metal Gear Sloth --- .../Accessible/AiReachableSystem.cs | 56 ++++++++++++++++++- .../Accessible/PathfindingRegion.cs | 4 ++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/AiReachableSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/AiReachableSystem.cs index b7f0322969..865ac3adc1 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/AiReachableSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/AiReachableSystem.cs @@ -74,6 +74,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible // Plus this way we can check if everything is equal except for vision so an entity with a lower vision radius can use an entity with a higher vision radius' cached result private Dictionary Regions)>> _cachedAccessible = new Dictionary)>>(); + + private readonly List _queuedCacheDeletions = new List(); #if DEBUG private int _runningCacheIdx = 0; @@ -88,7 +90,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible #endif _mapmanager.OnGridRemoved += GridRemoved; } - + private void GridRemoved(GridId gridId) { _regions.Remove(gridId); @@ -115,6 +117,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible } #endif _queuedUpdates.Clear(); + + foreach (var region in _queuedCacheDeletions) + { + ClearCache(region); + } + + _queuedCacheDeletions.Clear(); } public override void Shutdown() @@ -123,6 +132,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible _queuedUpdates.Clear(); _regions.Clear(); _cachedAccessible.Clear(); + _queuedCacheDeletions.Clear(); } public void ResettingCleanup() @@ -130,6 +140,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible _queuedUpdates.Clear(); _regions.Clear(); _cachedAccessible.Clear(); + _queuedCacheDeletions.Clear(); } private void RecalculateNodeRegions(PathfindingChunkUpdateMessage message) @@ -216,7 +227,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible public HashSet GetReachableRegions(ReachableArgs reachableArgs, PathfindingRegion region) { // if we're on a node that's not tracked at all atm then region will be null - if (region == null) + if (region == null || region.Deleted) { return new HashSet(); } @@ -345,7 +356,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible foreach (var neighbor in region.Neighbors) { - if (closedSet.Contains(neighbor)) + if (closedSet.Contains(neighbor) || neighbor.Deleted) { continue; } @@ -530,6 +541,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible } source.Shutdown(); + // This doesn't check the cachedaccessible to see if it's reachable but maybe it should? + // Although currently merge gets spammed so maybe when some other stuff is improved + // MergeInto is also only called by GenerateRegions currently so nothing should hold onto the original region _regions[source.ParentChunk.GridId][source.ParentChunk].Remove(source); foreach (var node in target.Nodes) @@ -538,6 +552,40 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible } } + /// + /// Remove the cached accessibility lookup for this region + /// + /// + private void ClearCache(PathfindingRegion region) + { + // Need to forcibly clear cache for ourself and anything that includes us + foreach (var (_, cachedRegions) in _cachedAccessible) + { + if (cachedRegions.ContainsKey(region)) + { + cachedRegions.Remove(region); + } + + // Seemed like the safest way to remove this + // We could just have GetVisionAccessible remove us if it can tell we're deleted but that + // seems like it could be unreliable + var regionsToClear = new List(); + + foreach (var (otherRegion, cache) in cachedRegions) + { + if (cache.Regions.Contains(region)) + { + regionsToClear.Add(otherRegion); + } + } + + foreach (var otherRegion in regionsToClear) + { + cachedRegions.Remove(otherRegion); + } + } + } + /// /// Generate all of the regions within a chunk /// @@ -554,8 +602,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible { foreach (var region in regions) { + _queuedCacheDeletions.Add(region); region.Shutdown(); } + _regions[chunk.GridId].Remove(chunk); } diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/PathfindingRegion.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/PathfindingRegion.cs index faea8c75d9..ea4b8c2ad6 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/PathfindingRegion.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Accessible/PathfindingRegion.cs @@ -36,6 +36,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible public HashSet Nodes => _nodes; private HashSet _nodes; + public bool Deleted { get; private set; } + public PathfindingRegion(PathfindingNode originNode, HashSet nodes, bool isDoor = false) { OriginNode = originNode; @@ -53,6 +55,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible var neighbor = neighbors[i]; neighbor.Neighbors.Remove(this); } + + Deleted = true; } ///