Fixes exceptions in AiReachableSystem on round restart.
This commit is contained in:
@@ -43,42 +43,42 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
[Dependency] private IGameTiming _gameTiming;
|
[Dependency] private IGameTiming _gameTiming;
|
||||||
#pragma warning restore 649
|
#pragma warning restore 649
|
||||||
private PathfindingSystem _pathfindingSystem;
|
private PathfindingSystem _pathfindingSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queued region updates
|
/// Queued region updates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private HashSet<PathfindingChunk> _queuedUpdates = new HashSet<PathfindingChunk>();
|
private HashSet<PathfindingChunk> _queuedUpdates = new HashSet<PathfindingChunk>();
|
||||||
|
|
||||||
// Oh god the nesting. Shouldn't need to go beyond this
|
// Oh god the nesting. Shouldn't need to go beyond this
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The corresponding regions for each PathfindingChunk.
|
/// The corresponding regions for each PathfindingChunk.
|
||||||
/// Regions are groups of nodes with the same profile (for pathfinding purposes)
|
/// Regions are groups of nodes with the same profile (for pathfinding purposes)
|
||||||
/// i.e. same collision, not-space, same access, etc.
|
/// i.e. same collision, not-space, same access, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<GridId, Dictionary<PathfindingChunk, HashSet<PathfindingRegion>>> _regions =
|
private Dictionary<GridId, Dictionary<PathfindingChunk, HashSet<PathfindingRegion>>> _regions =
|
||||||
new Dictionary<GridId, Dictionary<PathfindingChunk, HashSet<PathfindingRegion>>>();
|
new Dictionary<GridId, Dictionary<PathfindingChunk, HashSet<PathfindingRegion>>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum time for the cached reachable regions to be stored
|
/// Minimum time for the cached reachable regions to be stored
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float MinCacheTime = 1.0f;
|
private const float MinCacheTime = 1.0f;
|
||||||
|
|
||||||
// Cache what regions are accessible from this region. Cached per ReachableArgs
|
// Cache what regions are accessible from this region. Cached per ReachableArgs
|
||||||
// so multiple entities in the same region with the same args should all be able to share their reachable lookup
|
// so multiple entities in the same region with the same args should all be able to share their reachable lookup
|
||||||
// Also need to store when we cached it to know if it's stale if the chunks have updated
|
// Also need to store when we cached it to know if it's stale if the chunks have updated
|
||||||
|
|
||||||
// TODO: There's probably a more memory-efficient way to cache this
|
// TODO: There's probably a more memory-efficient way to cache this
|
||||||
// Then again, there's likely also a more memory-efficient way to implement regions.
|
// Then again, there's likely also a more memory-efficient way to implement regions.
|
||||||
|
|
||||||
// Also, didn't use a dictionary because there didn't seem to be a clean way to do the lookup
|
// Also, didn't use a dictionary because there didn't seem to be a clean way to do the lookup
|
||||||
// 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>)>>();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private int _runningCacheIdx = 0;
|
private int _runningCacheIdx = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
_pathfindingSystem = Get<PathfindingSystem>();
|
_pathfindingSystem = Get<PathfindingSystem>();
|
||||||
@@ -86,6 +86,12 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
SubscribeLocalEvent<PlayerAttachSystemMessage>(SendDebugMessage);
|
SubscribeLocalEvent<PlayerAttachSystemMessage>(SendDebugMessage);
|
||||||
#endif
|
#endif
|
||||||
|
_mapmanager.OnGridRemoved += GridRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GridRemoved(GridId gridId)
|
||||||
|
{
|
||||||
|
_regions.Remove(gridId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
@@ -165,7 +171,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
targetNode = node;
|
targetNode = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CanAccess(entity, targetNode);
|
return CanAccess(entity, targetNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +181,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entityTile = _mapmanager.GetGrid(entity.Transform.GridID).GetTileRef(entity.Transform.GridPosition);
|
var entityTile = _mapmanager.GetGrid(entity.Transform.GridID).GetTileRef(entity.Transform.GridPosition);
|
||||||
var entityNode = _pathfindingSystem.GetNode(entityTile);
|
var entityNode = _pathfindingSystem.GetNode(entityTile);
|
||||||
var entityRegion = GetRegion(entityNode);
|
var entityRegion = GetRegion(entityNode);
|
||||||
@@ -207,7 +213,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
return new HashSet<PathfindingRegion>();
|
return new HashSet<PathfindingRegion>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var cachedArgs = GetCachedArgs(reachableArgs);
|
var cachedArgs = GetCachedArgs(reachableArgs);
|
||||||
(TimeSpan CacheTime, HashSet<PathfindingRegion> Regions) cached;
|
(TimeSpan CacheTime, HashSet<PathfindingRegion> Regions) cached;
|
||||||
|
|
||||||
@@ -239,7 +245,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
private ReachableArgs GetCachedArgs(ReachableArgs accessibleArgs)
|
private ReachableArgs GetCachedArgs(ReachableArgs accessibleArgs)
|
||||||
{
|
{
|
||||||
ReachableArgs foundArgs = null;
|
ReachableArgs foundArgs = null;
|
||||||
|
|
||||||
foreach (var (cachedAccessible, _) in _cachedAccessible)
|
foreach (var (cachedAccessible, _) in _cachedAccessible)
|
||||||
{
|
{
|
||||||
if (Equals(cachedAccessible.Access, accessibleArgs.Access) &&
|
if (Equals(cachedAccessible.Access, accessibleArgs.Access) &&
|
||||||
@@ -269,7 +275,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
_cachedAccessible.Add(accessibleArgs, new Dictionary<PathfindingRegion, (TimeSpan, HashSet<PathfindingRegion>)>());
|
_cachedAccessible.Add(accessibleArgs, new Dictionary<PathfindingRegion, (TimeSpan, HashSet<PathfindingRegion>)>());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cachedArgs.TryGetValue(region, out var regionCache))
|
if (!cachedArgs.TryGetValue(region, out var regionCache))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -287,7 +293,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
foreach (var accessibleRegion in regionCache.Regions)
|
foreach (var accessibleRegion in regionCache.Regions)
|
||||||
{
|
{
|
||||||
if (checkedAccess.Contains(accessibleRegion)) continue;
|
if (checkedAccess.Contains(accessibleRegion)) continue;
|
||||||
|
|
||||||
// Any applicable chunk has been invalidated OR one of our neighbors has been invalidated (i.e. new connections)
|
// Any applicable chunk has been invalidated OR one of our neighbors has been invalidated (i.e. new connections)
|
||||||
// TODO: Could look at storing the TimeSpan directly on the region so our neighbor can tell us straight-up
|
// TODO: Could look at storing the TimeSpan directly on the region so our neighbor can tell us straight-up
|
||||||
if (accessibleRegion.ParentChunk.LastUpdate > regionCache.CacheTime)
|
if (accessibleRegion.ParentChunk.LastUpdate > regionCache.CacheTime)
|
||||||
@@ -336,7 +342,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distance is an approximation here so we'll be generous with it
|
// Distance is an approximation here so we'll be generous with it
|
||||||
// TODO: Could do better; the fewer nodes the better it is.
|
// TODO: Could do better; the fewer nodes the better it is.
|
||||||
if (!neighbor.RegionTraversable(reachableArgs) ||
|
if (!neighbor.RegionTraversable(reachableArgs) ||
|
||||||
@@ -345,12 +351,12 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
closedSet.Add(neighbor);
|
closedSet.Add(neighbor);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
openSet.Enqueue(neighbor);
|
openSet.Enqueue(neighbor);
|
||||||
accessible.Add(neighbor);
|
accessible.Add(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (_gameTiming.CurTime, accessible);
|
return (_gameTiming.CurTime, accessible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,7 +376,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
var directionNode = node.GetNeighbor(direction);
|
var directionNode = node.GetNeighbor(direction);
|
||||||
if (directionNode == null) continue;
|
if (directionNode == null) continue;
|
||||||
|
|
||||||
var directionRegion = GetRegion(directionNode);
|
var directionRegion = GetRegion(directionNode);
|
||||||
if (directionRegion == null || directionRegion == region) continue;
|
if (directionRegion == null || directionRegion == region) continue;
|
||||||
|
|
||||||
@@ -403,7 +409,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
// On the other hand, you might need O(n) lookups on regions for each chunk, though it's probably not too bad with smaller chunk sizes?
|
// On the other hand, you might need O(n) lookups on regions for each chunk, though it's probably not too bad with smaller chunk sizes?
|
||||||
// Someone smarter than me will know better
|
// Someone smarter than me will know better
|
||||||
var parentChunk = node.ParentChunk;
|
var parentChunk = node.ParentChunk;
|
||||||
|
|
||||||
// No guarantee the node even has a region yet (if we're doing neighbor lookups)
|
// No guarantee the node even has a region yet (if we're doing neighbor lookups)
|
||||||
if (!_regions[parentChunk.GridId].TryGetValue(parentChunk, out var regions))
|
if (!_regions[parentChunk.GridId].TryGetValue(parentChunk, out var regions))
|
||||||
{
|
{
|
||||||
@@ -460,10 +466,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
PathfindingRegion bottomRegion;
|
PathfindingRegion bottomRegion;
|
||||||
|
|
||||||
// We'll check if our left or down neighbors are already in a region and join them
|
// We'll check if our left or down neighbors are already in a region and join them
|
||||||
|
|
||||||
// Is left node valid to connect to
|
// Is left node valid to connect to
|
||||||
if (leftNeighbor != null &&
|
if (leftNeighbor != null &&
|
||||||
existingRegions.TryGetValue(leftNeighbor, out leftRegion) &&
|
existingRegions.TryGetValue(leftNeighbor, out leftRegion) &&
|
||||||
!leftRegion.IsDoor)
|
!leftRegion.IsDoor)
|
||||||
{
|
{
|
||||||
// We'll try and connect the left node's region to the bottom region if they're separate (yay merge)
|
// We'll try and connect the left node's region to the bottom region if they're separate (yay merge)
|
||||||
@@ -483,8 +489,8 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Is bottom node valid to connect to
|
//Is bottom node valid to connect to
|
||||||
if (bottomNeighbor != null &&
|
if (bottomNeighbor != null &&
|
||||||
existingRegions.TryGetValue(bottomNeighbor, out bottomRegion) &&
|
existingRegions.TryGetValue(bottomNeighbor, out bottomRegion) &&
|
||||||
!bottomRegion.IsDoor)
|
!bottomRegion.IsDoor)
|
||||||
{
|
{
|
||||||
bottomRegion.Add(node);
|
bottomRegion.Add(node);
|
||||||
@@ -492,13 +498,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
UpdateRegionEdge(bottomRegion, node);
|
UpdateRegionEdge(bottomRegion, node);
|
||||||
return bottomRegion;
|
return bottomRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we can't join an existing region then we'll make our own
|
// If we can't join an existing region then we'll make our own
|
||||||
var newRegion = new PathfindingRegion(node, new HashSet<PathfindingNode> {node}, node.AccessReaders.Count > 0);
|
var newRegion = new PathfindingRegion(node, new HashSet<PathfindingNode> {node}, node.AccessReaders.Count > 0);
|
||||||
_regions[parentChunk.GridId][parentChunk].Add(newRegion);
|
_regions[parentChunk.GridId][parentChunk].Add(newRegion);
|
||||||
existingRegions.Add(node, newRegion);
|
existingRegions.Add(node, newRegion);
|
||||||
UpdateRegionEdge(newRegion, node);
|
UpdateRegionEdge(newRegion, node);
|
||||||
|
|
||||||
return newRegion;
|
return newRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,7 +530,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
UpdateRegionEdge(target, node);
|
UpdateRegionEdge(target, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate all of the regions within a chunk
|
/// Generate all of the regions within a chunk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -564,7 +570,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkRegions.Add(region);
|
chunkRegions.Add(region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -587,8 +593,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
var debugResult = new Dictionary<int, Dictionary<int, List<Vector2>>>();
|
var debugResult = new Dictionary<int, Dictionary<int, List<Vector2>>>();
|
||||||
var chunkIdx = 0;
|
var chunkIdx = 0;
|
||||||
var regionIdx = 0;
|
var regionIdx = 0;
|
||||||
|
|
||||||
foreach (var (_, regions) in _regions[gridId])
|
if (!_regions.TryGetValue(gridId, out var dict))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (_, regions) in dict)
|
||||||
{
|
{
|
||||||
var debugRegions = new Dictionary<int, List<Vector2>>();
|
var debugRegions = new Dictionary<int, List<Vector2>>();
|
||||||
debugResult.Add(chunkIdx, debugRegions);
|
debugResult.Add(chunkIdx, debugRegions);
|
||||||
@@ -622,15 +633,15 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
{
|
{
|
||||||
var grid = _mapmanager.GetGrid(gridId);
|
var grid = _mapmanager.GetGrid(gridId);
|
||||||
var debugResult = new Dictionary<int, List<Vector2>>();
|
var debugResult = new Dictionary<int, List<Vector2>>();
|
||||||
|
|
||||||
foreach (var region in regions)
|
foreach (var region in regions)
|
||||||
{
|
{
|
||||||
debugResult.Add(_runningCacheIdx, new List<Vector2>());
|
debugResult.Add(_runningCacheIdx, new List<Vector2>());
|
||||||
|
|
||||||
foreach (var node in region.Nodes)
|
foreach (var node in region.Nodes)
|
||||||
{
|
{
|
||||||
var nodeVector = grid.GridTileToLocal(node.TileRef.GridIndices).ToMapPos(_mapmanager);
|
var nodeVector = grid.GridTileToLocal(node.TileRef.GridIndices).ToMapPos(_mapmanager);
|
||||||
|
|
||||||
debugResult[_runningCacheIdx].Add(nodeVector);
|
debugResult[_runningCacheIdx].Add(nodeVector);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,4 +652,4 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Accessible
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user