Better AI reachable cleanup (#1507)

Haven't profiled so can't say if it definitely fixes the leak.

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2020-07-28 00:11:07 +10:00
committed by GitHub
parent 0a82aba88e
commit 89e0925c32
2 changed files with 57 additions and 3 deletions

View File

@@ -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 // 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<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan CacheTime, HashSet<PathfindingRegion> Regions)>> _cachedAccessible = private Dictionary<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan CacheTime, HashSet<PathfindingRegion> Regions)>> _cachedAccessible =
new Dictionary<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan, HashSet<PathfindingRegion>)>>(); new Dictionary<ReachableArgs, Dictionary<PathfindingRegion, (TimeSpan, HashSet<PathfindingRegion>)>>();
private readonly List<PathfindingRegion> _queuedCacheDeletions = new List<PathfindingRegion>();
#if DEBUG #if DEBUG
private int _runningCacheIdx = 0; private int _runningCacheIdx = 0;
@@ -88,7 +90,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
#endif #endif
_mapmanager.OnGridRemoved += GridRemoved; _mapmanager.OnGridRemoved += GridRemoved;
} }
private void GridRemoved(GridId gridId) private void GridRemoved(GridId gridId)
{ {
_regions.Remove(gridId); _regions.Remove(gridId);
@@ -115,6 +117,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
} }
#endif #endif
_queuedUpdates.Clear(); _queuedUpdates.Clear();
foreach (var region in _queuedCacheDeletions)
{
ClearCache(region);
}
_queuedCacheDeletions.Clear();
} }
public override void Shutdown() public override void Shutdown()
@@ -123,6 +132,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_queuedUpdates.Clear(); _queuedUpdates.Clear();
_regions.Clear(); _regions.Clear();
_cachedAccessible.Clear(); _cachedAccessible.Clear();
_queuedCacheDeletions.Clear();
} }
public void ResettingCleanup() public void ResettingCleanup()
@@ -130,6 +140,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
_queuedUpdates.Clear(); _queuedUpdates.Clear();
_regions.Clear(); _regions.Clear();
_cachedAccessible.Clear(); _cachedAccessible.Clear();
_queuedCacheDeletions.Clear();
} }
private void RecalculateNodeRegions(PathfindingChunkUpdateMessage message) private void RecalculateNodeRegions(PathfindingChunkUpdateMessage message)
@@ -216,7 +227,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
public HashSet<PathfindingRegion> GetReachableRegions(ReachableArgs reachableArgs, PathfindingRegion region) public HashSet<PathfindingRegion> GetReachableRegions(ReachableArgs reachableArgs, PathfindingRegion region)
{ {
// if we're on a node that's not tracked at all atm then region will be null // 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<PathfindingRegion>(); return new HashSet<PathfindingRegion>();
} }
@@ -345,7 +356,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
foreach (var neighbor in region.Neighbors) foreach (var neighbor in region.Neighbors)
{ {
if (closedSet.Contains(neighbor)) if (closedSet.Contains(neighbor) || neighbor.Deleted)
{ {
continue; continue;
} }
@@ -530,6 +541,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
} }
source.Shutdown(); 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); _regions[source.ParentChunk.GridId][source.ParentChunk].Remove(source);
foreach (var node in target.Nodes) foreach (var node in target.Nodes)
@@ -538,6 +552,40 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
} }
} }
/// <summary>
/// Remove the cached accessibility lookup for this region
/// </summary>
/// <param name="region"></param>
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<PathfindingRegion>();
foreach (var (otherRegion, cache) in cachedRegions)
{
if (cache.Regions.Contains(region))
{
regionsToClear.Add(otherRegion);
}
}
foreach (var otherRegion in regionsToClear)
{
cachedRegions.Remove(otherRegion);
}
}
}
/// <summary> /// <summary>
/// Generate all of the regions within a chunk /// Generate all of the regions within a chunk
/// </summary> /// </summary>
@@ -554,8 +602,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
{ {
foreach (var region in regions) foreach (var region in regions)
{ {
_queuedCacheDeletions.Add(region);
region.Shutdown(); region.Shutdown();
} }
_regions[chunk.GridId].Remove(chunk); _regions[chunk.GridId].Remove(chunk);
} }

View File

@@ -36,6 +36,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
public HashSet<PathfindingNode> Nodes => _nodes; public HashSet<PathfindingNode> Nodes => _nodes;
private HashSet<PathfindingNode> _nodes; private HashSet<PathfindingNode> _nodes;
public bool Deleted { get; private set; }
public PathfindingRegion(PathfindingNode originNode, HashSet<PathfindingNode> nodes, bool isDoor = false) public PathfindingRegion(PathfindingNode originNode, HashSet<PathfindingNode> nodes, bool isDoor = false)
{ {
OriginNode = originNode; OriginNode = originNode;
@@ -53,6 +55,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
var neighbor = neighbors[i]; var neighbor = neighbors[i];
neighbor.Neighbors.Remove(this); neighbor.Neighbors.Remove(this);
} }
Deleted = true;
} }
/// <summary> /// <summary>