diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs index 7665c3070a..dc47c8fbc2 100644 --- a/Content.Server/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs @@ -84,7 +84,7 @@ namespace Content.Server.Abilities.Mime } // Check there are no mobs there - foreach (var entity in _lookupSystem.GetEntitiesIntersecting(tile.Value)) + foreach (var entity in _lookupSystem.GetEntitiesIntersecting(tile.Value, 0f)) { if (HasComp(entity) && entity != uid) { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs index d5f810c67c..e7cb278d13 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.HighPressureDelta.cs @@ -138,7 +138,7 @@ namespace Content.Server.Atmos.EntitySystems tile.PressureSpecificTarget = curTile; } - foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) + foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices, 0f)) { // Ideally containers would have their own EntityQuery internally or something given recursively it may need to slam GetComp anyway. // Also, don't care about static bodies (but also due to collisionwakestate can't query dynamic directly atm). diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index 575391cdfd..1b44f6e819 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -167,9 +167,9 @@ namespace Content.Server.Atmos.EntitySystems var fireEvent = new TileFireEvent(tile.Hotspot.Temperature, tile.Hotspot.Volume); - foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices)) + foreach (var entity in _lookup.GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices, 0f)) { - RaiseLocalEvent(entity, ref fireEvent, false); + RaiseLocalEvent(entity, ref fireEvent); } } } diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index a2bd5decbd..1ac40375a4 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -34,7 +34,7 @@ public sealed class CleanTileReaction : ITileReaction FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) { var entMan = IoCManager.Resolve(); - var entities = entMan.System().GetEntitiesIntersecting(tile).ToArray(); + var entities = entMan.System().GetEntitiesIntersecting(tile, 0f).ToArray(); var puddleQuery = entMan.GetEntityQuery(); var solutionContainerSystem = entMan.System(); // Multiply as the amount we can actually purge is higher than the react amount. diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index 831ca9c72b..241ec1daa2 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -208,7 +208,7 @@ public sealed class SmokeSystem : EntitySystem var tile = mapGrid.GetTileRef(xform.Coordinates.ToVector2i(EntityManager, _mapManager)); var solutionFraction = 1 / Math.Floor(frameTime); - var ents = _lookup.GetEntitiesIntersecting(tile, LookupFlags.Uncontained).ToArray(); + var ents = _lookup.GetEntitiesIntersecting(tile, 0f, flags: LookupFlags.Uncontained).ToArray(); foreach (var reagentQuantity in solution.Contents.ToArray()) { diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs index 3cf0951d64..b427a13d8d 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs @@ -50,6 +50,7 @@ public sealed partial class PathfindingSystem SubscribeLocalEvent(OnGridPathPause); SubscribeLocalEvent(OnGridPathShutdown); SubscribeLocalEvent(OnCollisionChange); + SubscribeLocalEvent(OnCollisionLayerChange); SubscribeLocalEvent(OnBodyTypeChange); SubscribeLocalEvent(OnTileChange); SubscribeLocalEvent(OnMoveEvent); @@ -95,7 +96,7 @@ public sealed partial class PathfindingSystem _stopwatch.Restart(); var options = new ParallelOptions() { - MaxDegreeOfParallelism = _parallel.ParallelProcessCount, + MaxDegreeOfParallelism = 1, }; // We defer chunk updates because rebuilding a navmesh is hella costly @@ -238,17 +239,18 @@ public sealed partial class PathfindingSystem } } - private bool IsBodyRelevant(PhysicsComponent body) + private bool IsBodyRelevant(FixturesComponent fixtures) { - if (!body.Hard || body.BodyType != BodyType.Static) + foreach (var fixture in fixtures.Fixtures.Values) { - return false; - } + if (!fixture.Hard) + continue; - if ((body.CollisionMask & PathfindingCollisionLayer) != 0x0 || - (body.CollisionLayer & PathfindingCollisionMask) != 0x0) - { - return true; + if ((fixture.CollisionMask & PathfindingCollisionLayer) != 0x0 || + (fixture.CollisionLayer & PathfindingCollisionMask) != 0x0) + { + return true; + } } return false; @@ -256,35 +258,42 @@ public sealed partial class PathfindingSystem private void OnCollisionChange(ref CollisionChangeEvent ev) { - if (!IsBodyRelevant(ev.Body)) - return; - var xform = Transform(ev.Body.Owner); if (xform.GridUid == null) return; // This will also rebuild on door open / closes which I think is good? - DirtyChunk(xform.GridUid.Value, xform.Coordinates); + var aabb = _lookup.GetAABBNoContainer(ev.Body.Owner, xform.Coordinates.Position, xform.LocalRotation); + DirtyChunkArea(xform.GridUid.Value, aabb); + } + + private void OnCollisionLayerChange(ref CollisionLayerChangeEvent ev) + { + var xform = Transform(ev.Body.Owner); + + if (xform.GridUid == null) + return; + + var aabb = _lookup.GetAABBNoContainer(ev.Body.Owner, xform.Coordinates.Position, xform.LocalRotation); + DirtyChunkArea(xform.GridUid.Value, aabb); } private void OnBodyTypeChange(ref PhysicsBodyTypeChangedEvent ev) { - if (ev.Component.CanCollide && - IsBodyRelevant(ev.Component) && - TryComp(ev.Entity, out var xform) && + if (TryComp(ev.Entity, out var xform) && xform.GridUid != null) { - DirtyChunk(xform.GridUid.Value, xform.Coordinates); + var aabb = _lookup.GetAABBNoContainer(ev.Entity, xform.Coordinates.Position, xform.LocalRotation); + DirtyChunkArea(xform.GridUid.Value, aabb); } } private void OnMoveEvent(ref MoveEvent ev) { - if (!TryComp(ev.Sender, out var body) || - body.BodyType != BodyType.Static || - HasComp(ev.Sender) || - ev.OldPosition.Equals(ev.NewPosition)) + if (!TryComp(ev.Sender, out var fixtures) || + !IsBodyRelevant(fixtures) || + HasComp(ev.Sender)) { return; } @@ -294,34 +303,16 @@ public sealed partial class PathfindingSystem ? gridUid : ev.OldPosition.GetGridUid(EntityManager); - // Not on a grid at all so just ignore. - if (oldGridUid == gridUid && oldGridUid == null) + if (oldGridUid != null && oldGridUid != gridUid) { - return; - } - - if (oldGridUid != null && gridUid != null) - { - // If the chunk hasn't changed then just dirty that one. - var oldOrigin = GetOrigin(ev.OldPosition, oldGridUid.Value); - var origin = GetOrigin(ev.NewPosition, gridUid.Value); - - if (oldOrigin == origin) - { - // TODO: Don't need to transform again numpty. - DirtyChunk(oldGridUid.Value, ev.NewPosition); - return; - } - } - - if (oldGridUid != null) - { - DirtyChunk(oldGridUid.Value, ev.OldPosition); + var aabb = _lookup.GetAABBNoContainer(ev.Sender, ev.OldPosition.Position, ev.OldRotation); + DirtyChunkArea(oldGridUid.Value, aabb); } if (gridUid != null) { - DirtyChunk(gridUid.Value, ev.NewPosition); + var aabb = _lookup.GetAABBNoContainer(ev.Sender, ev.NewPosition.Position, ev.NewRotation); + DirtyChunkArea(gridUid.Value, aabb); } } @@ -364,6 +355,30 @@ public sealed partial class PathfindingSystem chunks.Add(GetOrigin(coordinates, gridUid)); } + private void DirtyChunkArea(EntityUid gridUid, Box2 aabb) + { + if (!TryComp(gridUid, out var comp)) + return; + + var currentTime = _timing.CurTime; + + if (comp.NextUpdate < currentTime) + comp.NextUpdate = currentTime + UpdateCooldown; + + var chunks = comp.DirtyChunks; + + // This assumes you never have bounds equal to or larger than 2 * ChunkSize. + var corners = new Vector2[] { aabb.BottomLeft, aabb.TopRight, aabb.BottomRight, aabb.TopLeft }; + foreach (var corner in corners) + { + var sampledPoint = new Vector2i( + (int) Math.Floor((corner.X) / ChunkSize), + (int) Math.Floor((corner.Y) / ChunkSize)); + + chunks.Add(sampledPoint); + } + } + private GridPathfindingChunk GetChunk(Vector2i origin, EntityUid uid, GridPathfindingComponent? component = null) { if (!Resolve(uid, ref component)) @@ -445,18 +460,18 @@ public sealed partial class PathfindingSystem // var isBorder = x < 0 || y < 0 || x == ChunkSize - 1 || y == ChunkSize - 1; tileEntities.Clear(); - var anchored = grid.GetAnchoredEntitiesEnumerator(tilePos); + var available = _lookup.GetEntitiesIntersecting(tile); - while (anchored.MoveNext(out var ent)) + foreach (var ent in available) { // Irrelevant for pathfinding - if (!physicsQuery.TryGetComponent(ent, out var body) || - !IsBodyRelevant(body)) + if (!fixturesQuery.TryGetComponent(ent, out var fixtures) || + !IsBodyRelevant(fixtures)) { continue; } - tileEntities.Add(ent.Value); + tileEntities.Add(ent); } for (var subX = 0; subX < SubStep; subX++) @@ -485,7 +500,9 @@ public sealed partial class PathfindingSystem if (!fixture.Hard || (collisionMask & fixture.CollisionMask) == fixture.CollisionMask && (collisionLayer & fixture.CollisionLayer) == fixture.CollisionLayer) + { continue; + } // Do an AABB check first as it's probably faster, then do an actual point check. var intersects = false; diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs index d6e3616147..fc9dc87c31 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.cs @@ -45,6 +45,7 @@ namespace Content.Server.NPC.Pathfinding [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly DestructibleSystem _destructible = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index dcfd7ecc5f..6fbc2baa82 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -254,7 +254,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem // Dependency issues across threads. var options = new ParallelOptions { - MaxDegreeOfParallelism = 1, + MaxDegreeOfParallelism = _parallel.ParallelProcessCount, }; var curTime = _timing.CurTime; diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index 65c47eb357..d0a741ec76 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -125,7 +125,7 @@ public sealed class ConveyorController : SharedConveyorController if (beltTileRef != null) { - var intersecting = Lookup.GetEntitiesIntersecting(beltTileRef.Value); + var intersecting = Lookup.GetEntitiesIntersecting(beltTileRef.Value, 0f); foreach (var entity in intersecting) { diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs index 7e9d132974..7a7cf02e1d 100644 --- a/Content.Shared/Blocking/BlockingSystem.cs +++ b/Content.Shared/Blocking/BlockingSystem.cs @@ -172,7 +172,7 @@ public sealed partial class BlockingSystem : EntitySystem var playerTileRef = xform.Coordinates.GetTileRef(); if (playerTileRef != null) { - var intersecting = _lookup.GetEntitiesIntersecting(playerTileRef.Value); + var intersecting = _lookup.GetEntitiesIntersecting(playerTileRef.Value, 0f); var mobQuery = GetEntityQuery(); foreach (var uid in intersecting) {