]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Replace NavMap dictionaries with int[] (#27602)
authorLeon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Thu, 2 May 2024 02:51:21 +0000 (14:51 +1200)
committerGitHub <noreply@github.com>
Thu, 2 May 2024 02:51:21 +0000 (12:51 +1000)
* Replace NavMap dictionaries with int[]

* Remove badly named const

* Remove unnecessary offset

* Prioritize airlocks

Content.Client/Pinpointer/NavMapSystem.cs
Content.Client/Pinpointer/UI/NavMapControl.cs
Content.Client/Power/PowerMonitoringConsoleNavMapControl.cs
Content.Server/Pinpointer/NavMapSystem.cs
Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs
Content.Shared/Atmos/AtmosDirection.cs
Content.Shared/Pinpointer/NavMapComponent.cs
Content.Shared/Pinpointer/SharedNavMapSystem.cs
Content.Shared/Power/SharedPowerMonitoringConsoleSystem.cs
Content.Shared/Tag/TagSystem.cs

index c80e7e85e280dd7943ebeb34f9d12d737396a077..e33bc5d3291781d3efb4c4b5c7749dc0374a0d32 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.Atmos;
 using Content.Shared.Pinpointer;
 using Robust.Shared.GameStates;
 
@@ -25,12 +24,6 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
                 if (!state.AllChunks!.Contains(index))
                     component.Chunks.Remove(index);
             }
-
-            foreach (var beacon in component.Beacons)
-            {
-                if (!state.AllBeacons!.Contains(beacon))
-                    component.Beacons.Remove(beacon);
-            }
         }
         else
         {
@@ -39,34 +32,19 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
                 if (!state.Chunks.ContainsKey(index))
                     component.Chunks.Remove(index);
             }
-
-            foreach (var beacon in component.Beacons)
-            {
-                if (!state.Beacons.Contains(beacon))
-                    component.Beacons.Remove(beacon);
-            }
         }
 
         foreach (var (origin, chunk) in state.Chunks)
         {
             var newChunk = new NavMapChunk(origin);
-
-            for (var i = 0; i < NavMapComponent.Categories; i++)
-            {
-                var newData = chunk[i];
-
-                if (newData == null)
-                    continue;
-
-                newChunk.TileData[i] = new(newData);
-            }
-
+            Array.Copy(chunk, newChunk.TileData, chunk.Length);
             component.Chunks[origin] = newChunk;
         }
 
-        foreach (var beacon in state.Beacons)
+        component.Beacons.Clear();
+        foreach (var (nuid, beacon) in state.Beacons)
         {
-            component.Beacons.Add(beacon);
+            component.Beacons[nuid] = beacon;
         }
     }
 }
index 5aa20b688148704b158e2655f14cb9135af89030..f4d2f8e2fb8c729c7e9a467a1f08f8f0b8b341e0 100644 (file)
@@ -18,6 +18,7 @@ using System.Numerics;
 using JetBrains.Annotations;
 using Content.Shared.Atmos;
 using System.Linq;
+using Robust.Shared.Utility;
 
 namespace Content.Client.Pinpointer.UI;
 
@@ -71,10 +72,10 @@ public partial class NavMapControl : MapGridControl
     protected float BackgroundOpacity = 0.9f;
     private int _targetFontsize = 8;
 
-    protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookup = new();
-    protected Dictionary<(int, Vector2i), (int, Vector2i)> HorizLinesLookupReversed = new();
-    protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookup = new();
-    protected Dictionary<(int, Vector2i), (int, Vector2i)> VertLinesLookupReversed = new();
+    private Dictionary<Vector2i, Vector2i> _horizLines = new();
+    private Dictionary<Vector2i, Vector2i> _horizLinesReversed = new();
+    private Dictionary<Vector2i, Vector2i> _vertLines = new();
+    private Dictionary<Vector2i, Vector2i> _vertLinesReversed = new();
 
     // Components
     private NavMapComponent? _navMap;
@@ -376,7 +377,7 @@ public partial class NavMapControl : MapGridControl
             var fontSize = (int) Math.Round(1 / WorldRange * DefaultDisplayedRange * UIScale * _targetFontsize, 0);
             var font = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), fontSize);
 
-            foreach (var beacon in _navMap.Beacons)
+            foreach (var beacon in _navMap.Beacons.Values)
             {
                 var position = beacon.Position - offset;
                 position = ScalePosition(position with { Y = -position.Y });
@@ -485,147 +486,105 @@ public partial class NavMapControl : MapGridControl
             return;
 
         // We'll use the following dictionaries to combine collinear wall lines
-        HorizLinesLookup.Clear();
-        HorizLinesLookupReversed.Clear();
-        VertLinesLookup.Clear();
-        VertLinesLookupReversed.Clear();
+        _horizLines.Clear();
+        _horizLinesReversed.Clear();
+        _vertLines.Clear();
+        _vertLinesReversed.Clear();
+
+        const int southMask = (int) AtmosDirection.South << (int) NavMapChunkType.Wall;
+        const int eastMask = (int) AtmosDirection.East << (int) NavMapChunkType.Wall;
+        const int westMask = (int) AtmosDirection.West << (int) NavMapChunkType.Wall;
+        const int northMask = (int) AtmosDirection.North << (int) NavMapChunkType.Wall;
 
         foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
         {
-            for (var j = 0; j < NavMapComponent.Categories; j++)
+            for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
             {
-                var category = (NavMapChunkType) j;
-
-                if (category != NavMapChunkType.Wall)
+                var tileData = chunk.TileData[i] & SharedNavMapSystem.WallMask;
+                if (tileData == 0)
                     continue;
 
-                var data = chunk.TileData[j];
+                tileData >>= (int) NavMapChunkType.Wall;
 
-                if (data == null)
-                    continue;
+                var relativeTile = SharedNavMapSystem.GetTileFromIndex(i);
+                var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
 
-                for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
+                if (tileData != SharedNavMapSystem.AllDirMask)
                 {
-                    var value = (ushort) Math.Pow(2, i);
-                    var mask = _navMapSystem.GetCombinedEdgesForChunk(data) & value;
-
-                    if (mask == 0x0)
-                        continue;
-
-                    var relativeTile = SharedNavMapSystem.GetTile(mask);
-                    var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
-
-                    if (!_navMapSystem.AllTileEdgesAreOccupied(data, relativeTile))
-                    {
-                        AddRectForThinWall(data, tile);
-                        continue;
-                    }
-
-                    tile = tile with { Y = -tile.Y };
-
-                    NavMapChunk? neighborChunk;
-                    bool neighbor;
-
-                    // North edge
-                    if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
-                    {
-                        _navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk);
-                        var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
-
-                        neighbor = neighborData != null &&
-                                   (neighborData[AtmosDirection.South] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
-                    }
-                    else
-                    {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Up);
-                        neighbor = (data[AtmosDirection.South] & flag) != 0x0;
-                    }
-
-                    if (!neighbor)
-                    {
-                        AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
-                            tile + new Vector2i(_grid.TileSize, -_grid.TileSize), HorizLinesLookup,
-                            HorizLinesLookupReversed);
-                    }
-
-                    // East edge
-                    if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
-                    {
-                        _navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk);
-                        var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
+                    AddRectForThinWall(tileData, tile);
+                    continue;
+                }
 
-                        neighbor = neighborData != null &&
-                                   (neighborData[AtmosDirection.West] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
-                    }
-                    else
-                    {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Right);
-                        neighbor = (data[AtmosDirection.West] & flag) != 0x0;
-                    }
+                tile = tile with { Y = -tile.Y };
+                NavMapChunk? neighborChunk;
 
-                    if (!neighbor)
-                    {
-                        AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
-                            tile + new Vector2i(_grid.TileSize, 0), VertLinesLookup, VertLinesLookupReversed);
-                    }
+                // North edge
+                var neighborData = 0;
+                if (relativeTile.Y != SharedNavMapSystem.ChunkSize - 1)
+                    neighborData = chunk.TileData[i+1];
+                else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Up, out neighborChunk))
+                    neighborData = neighborChunk.TileData[i + 1 - SharedNavMapSystem.ChunkSize];
 
-                    // South edge
-                    if (relativeTile.Y == 0)
-                    {
-                        _navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk);
-                        var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
+                if ((neighborData & southMask) == 0)
+                {
+                    AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize),
+                        tile + new Vector2i(_grid.TileSize, -_grid.TileSize), _horizLines,
+                        _horizLinesReversed);
+                }
 
-                        neighbor = neighborData != null &&
-                                   (neighborData[AtmosDirection.North] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, SharedNavMapSystem.ChunkSize - 1))) != 0x0;
-                    }
-                    else
-                    {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Down);
-                        neighbor = (data[AtmosDirection.North] & flag) != 0x0;
-                    }
+                // East edge
+                neighborData = 0;
+                if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1)
+                    neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize];
+                else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk))
+                    neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize];
 
-                    if (!neighbor)
-                    {
-                        AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup,
-                            HorizLinesLookupReversed);
-                    }
+                if ((neighborData & westMask) == 0)
+                {
+                    AddOrUpdateNavMapLine(tile + new Vector2i(_grid.TileSize, -_grid.TileSize),
+                        tile + new Vector2i(_grid.TileSize, 0), _vertLines, _vertLinesReversed);
+                }
 
-                    // West edge
-                    if (relativeTile.X == 0)
-                    {
-                        _navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk);
-                        var neighborData = neighborChunk?.TileData[(int) NavMapChunkType.Wall];
+                // South edge
+                neighborData = 0;
+                if (relativeTile.Y != 0)
+                    neighborData = chunk.TileData[i-1];
+                else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk))
+                    neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize];
 
-                        neighbor = neighborData != null &&
-                                   (neighborData[AtmosDirection.East] & SharedNavMapSystem.GetFlag(new Vector2i(SharedNavMapSystem.ChunkSize - 1, relativeTile.Y))) != 0x0;
-                    }
-                    else
-                    {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + Vector2i.Left);
-                        neighbor = (data[AtmosDirection.East] & flag) != 0x0;
-                    }
+                if ((neighborData & northMask) == 0)
+                {
+                    AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), _horizLines,
+                        _horizLinesReversed);
+                }
 
-                    if (!neighbor)
-                    {
-                        AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup,
-                            VertLinesLookupReversed);
-                    }
+                // West edge
+                neighborData = 0;
+                if (relativeTile.X != 0)
+                    neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize];
+                else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk))
+                    neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize];
 
-                    // Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
-                    TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
+                if ((neighborData & eastMask) == 0)
+                {
+                    AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, _vertLines,
+                        _vertLinesReversed);
                 }
+
+                // Add a diagonal line for interiors. Unless there are a lot of double walls, there is no point combining these
+                TileLines.Add((tile + new Vector2(0, -_grid.TileSize), tile + new Vector2(_grid.TileSize, 0)));
             }
         }
 
         // Record the combined lines
-        foreach (var (origin, terminal) in HorizLinesLookup)
+        foreach (var (origin, terminal) in _horizLines)
         {
-            TileLines.Add((origin.Item2, terminal.Item2));
+            TileLines.Add((origin, terminal));
         }
 
-        foreach (var (origin, terminal) in VertLinesLookup)
+        foreach (var (origin, terminal) in _vertLines)
         {
-            TileLines.Add((origin.Item2, terminal.Item2));
+            TileLines.Add((origin, terminal));
         }
     }
 
@@ -634,28 +593,23 @@ public partial class NavMapControl : MapGridControl
         if (_navMap == null || _grid == null)
             return;
 
-        foreach (var (chunkOrigin, chunk) in _navMap.Chunks)
+        foreach (var chunk in _navMap.Chunks.Values)
         {
-            var data = chunk.TileData[(int) NavMapChunkType.Airlock];
-
-            if (data == null)
-                continue;
-
-            for (var i = 0; i < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; i++)
+            for (var i = 0; i < SharedNavMapSystem.ArraySize; i++)
             {
-                var value = (int) Math.Pow(2, i);
-                var mask = _navMapSystem.GetCombinedEdgesForChunk(data) & value;
-
-                if (mask == 0x0)
+                var tileData = chunk.TileData[i] & SharedNavMapSystem.AirlockMask;
+                if (tileData == 0)
                     continue;
 
-                var relative = SharedNavMapSystem.GetTile(mask);
+                tileData >>= (int) NavMapChunkType.Airlock;
+
+                var relative = SharedNavMapSystem.GetTileFromIndex(i);
                 var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relative) * _grid.TileSize;
 
                 // If the edges of an airlock tile are not all occupied, draw a thin airlock for each edge
-                if (!_navMapSystem.AllTileEdgesAreOccupied(data, relative))
+                if (tileData != SharedNavMapSystem.AllDirMask)
                 {
-                    AddRectForThinAirlock(data, tile);
+                    AddRectForThinAirlock(tileData, tile);
                     continue;
                 }
 
@@ -669,108 +623,90 @@ public partial class NavMapControl : MapGridControl
         }
     }
 
-    private void AddRectForThinWall(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
+    private void AddRectForThinWall(int tileData, Vector2i tile)
     {
-        if (_navMapSystem == null || _grid == null)
-            return;
+        var leftTop = new Vector2(-0.5f, 0.5f - ThinWallThickness);
+        var rightBottom = new Vector2(0.5f, 0.5f);
 
-        var leftTop = new Vector2(-0.5f, -0.5f + ThinWallThickness);
-        var rightBottom = new Vector2(0.5f, -0.5f);
-
-        foreach (var (direction, mask) in tileData)
+        for (var i = 0; i < SharedNavMapSystem.Directions; i++)
         {
-            var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
-            var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
-
-            if ((mask & flag) == 0)
+            var dirMask = 1 << i;
+            if ((tileData & dirMask) == 0)
                 continue;
 
             var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
-            var angle = new Angle(0);
-
-            switch (direction)
-            {
-                case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f); break;
-                case AtmosDirection.South: angle = new Angle(MathF.PI); break;
-                case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
-            }
 
+            // TODO NAVMAP
+            // Consider using faster rotation operations, given that these are always 90 degree increments
+            var angle = -((AtmosDirection) dirMask).ToAngle();
             TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
         }
     }
 
-    private void AddRectForThinAirlock(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
+    private void AddRectForThinAirlock(int tileData, Vector2i tile)
     {
-        if (_navMapSystem == null || _grid == null)
-            return;
-
-        var leftTop = new Vector2(-0.5f + FullWallInstep, -0.5f + FullWallInstep + ThinDoorThickness);
-        var rightBottom = new Vector2(0.5f - FullWallInstep, -0.5f + FullWallInstep);
-        var centreTop = new Vector2(0f, -0.5f + FullWallInstep + ThinDoorThickness);
-        var centreBottom = new Vector2(0f, -0.5f + FullWallInstep);
+        var leftTop = new Vector2(-0.5f + FullWallInstep, 0.5f - FullWallInstep - ThinDoorThickness);
+        var rightBottom = new Vector2(0.5f - FullWallInstep, 0.5f - FullWallInstep);
+        var centreTop = new Vector2(0f, 0.5f - FullWallInstep - ThinDoorThickness);
+        var centreBottom = new Vector2(0f, 0.5f - FullWallInstep);
 
-        foreach (var (direction, mask) in tileData)
+        for (var i = 0; i < SharedNavMapSystem.Directions; i++)
         {
-            var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
-            var flag = (ushort) SharedNavMapSystem.GetFlag(relative);
-
-            if ((mask & flag) == 0)
+            var dirMask = 1 << i;
+            if ((tileData & dirMask) == 0)
                 continue;
 
             var tilePosition = new Vector2(tile.X + 0.5f, -tile.Y - 0.5f);
-            var angle = new Angle(0);
-
-            switch (direction)
-            {
-                case AtmosDirection.East: angle = new Angle(MathF.PI * 0.5f);break;
-                case AtmosDirection.South: angle = new Angle(MathF.PI); break;
-                case AtmosDirection.West: angle = new Angle(MathF.PI * -0.5f); break;
-            }
-
+            var angle = -((AtmosDirection) dirMask).ToAngle();
             TileRects.Add((angle.RotateVec(leftTop) + tilePosition, angle.RotateVec(rightBottom) + tilePosition));
             TileLines.Add((angle.RotateVec(centreTop) + tilePosition, angle.RotateVec(centreBottom) + tilePosition));
         }
     }
 
-    protected void AddOrUpdateNavMapLine
-        (Vector2i origin,
+    protected void AddOrUpdateNavMapLine(
+        Vector2i origin,
         Vector2i terminus,
-        Dictionary<(int, Vector2i), (int, Vector2i)> lookup,
-        Dictionary<(int, Vector2i), (int, Vector2i)> lookupReversed,
-        int index = 0)
+        Dictionary<Vector2i, Vector2i> lookup,
+        Dictionary<Vector2i, Vector2i> lookupReversed)
     {
-        (int, Vector2i) foundTermiusTuple;
-        (int, Vector2i) foundOriginTuple;
+        Vector2i foundTermius;
+        Vector2i foundOrigin;
 
-        if (lookup.TryGetValue((index, terminus), out foundTermiusTuple) &&
-            lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
+        // Does our new line end at the beginning of an existing line?
+        if (lookup.Remove(terminus, out foundTermius))
         {
-            lookup[foundOriginTuple] = foundTermiusTuple;
-            lookupReversed[foundTermiusTuple] = foundOriginTuple;
+            DebugTools.Assert(lookupReversed[foundTermius] == terminus);
 
-            lookup.Remove((index, terminus));
-            lookupReversed.Remove((index, origin));
-        }
-
-        else if (lookup.TryGetValue((index, terminus), out foundTermiusTuple))
-        {
-            lookup[(index, origin)] = foundTermiusTuple;
-            lookup.Remove((index, terminus));
-            lookupReversed[foundTermiusTuple] = (index, origin);
+            // Does our new line start at the end of an existing line?
+            if (lookupReversed.Remove(origin, out foundOrigin))
+            {
+                // Our new line just connects two existing lines
+                DebugTools.Assert(lookup[foundOrigin] == origin);
+                lookup[foundOrigin] = foundTermius;
+                lookupReversed[foundTermius] = foundOrigin;
+            }
+            else
+            {
+                // Our new line precedes an existing line, extending it further to the left
+                lookup[origin] = foundTermius;
+                lookupReversed[foundTermius] = origin;
+            }
+            return;
         }
 
-        else if (lookupReversed.TryGetValue((index, origin), out foundOriginTuple))
+        // Does our new line start at the end of an existing line?
+        if (lookupReversed.Remove(origin, out foundOrigin))
         {
-            lookupReversed[(index, terminus)] = foundOriginTuple;
-            lookupReversed.Remove(foundOriginTuple);
-            lookup[foundOriginTuple] = (index, terminus);
+            // Our new line just extends an existing line further to the right
+            DebugTools.Assert(lookup[foundOrigin] == origin);
+            lookup[foundOrigin] = terminus;
+            lookupReversed[terminus] = foundOrigin;
+            return;
         }
 
-        else
-        {
-            lookup.Add((index, origin), (index, terminus));
-            lookupReversed.Add((index, terminus), (index, origin));
-        }
+        // Completely disconnected line segment.
+        lookup.Add(origin, terminus);
+        lookupReversed.Add(terminus, origin);
     }
 
     protected Vector2 GetOffset()
index 3d94318be8260c6172984e418bb29051c12198c3..d5057416cf84ed2219e281141cf435141f91a365 100644 (file)
@@ -5,6 +5,7 @@ using Robust.Client.Graphics;
 using Robust.Shared.Collections;
 using Robust.Shared.Map.Components;
 using System.Numerics;
+using static Content.Shared.Power.SharedPowerMonitoringConsoleSystem;
 
 namespace Content.Client.Power;
 
@@ -26,6 +27,11 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
     public List<PowerMonitoringConsoleLine> PowerCableNetwork = new();
     public List<PowerMonitoringConsoleLine> FocusCableNetwork = new();
 
+    private Dictionary<Vector2i, Vector2i>[] _horizLines = [new(), new(), new()];
+    private Dictionary<Vector2i, Vector2i>[] _horizLinesReversed = [new(), new(), new()];
+    private Dictionary<Vector2i, Vector2i>[] _vertLines = [new(), new(), new()];
+    private Dictionary<Vector2i, Vector2i>[] _vertLinesReversed = [new(), new(), new()];
+
     private MapGridComponent? _grid;
 
     public PowerMonitoringConsoleNavMapControl() : base()
@@ -182,28 +188,32 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
         if (chunks == null)
             return decodedOutput;
 
-        // We'll use the following dictionaries to combine collinear power cable lines
-        HorizLinesLookup.Clear();
-        HorizLinesLookupReversed.Clear();
-        VertLinesLookup.Clear();
-        VertLinesLookupReversed.Clear();
+        Array.ForEach(_horizLines, x=> x.Clear());
+        Array.ForEach(_horizLinesReversed, x=> x.Clear());
+        Array.ForEach(_vertLines, x=> x.Clear());
+        Array.ForEach(_vertLinesReversed, x=> x.Clear());
 
-        foreach ((var chunkOrigin, var chunk) in chunks)
+        foreach (var (chunkOrigin, chunk) in chunks)
         {
-            for (int cableIdx = 0; cableIdx < chunk.PowerCableData.Length; cableIdx++)
+            for (var cableIdx = 0; cableIdx < 3; cableIdx++)
             {
+                var horizLines = _horizLines[cableIdx];
+                var horizLinesReversed = _horizLinesReversed[cableIdx];
+                var vertLines = _vertLines[cableIdx];
+                var vertLinesReversed = _vertLinesReversed[cableIdx];
+
                 var chunkMask = chunk.PowerCableData[cableIdx];
 
-                for (var chunkIdx = 0; chunkIdx < SharedNavMapSystem.ChunkSize * SharedNavMapSystem.ChunkSize; chunkIdx++)
+                for (var chunkIdx = 0; chunkIdx < ChunkSize * ChunkSize; chunkIdx++)
                 {
-                    var value = (int) Math.Pow(2, chunkIdx);
+                    var value = 1 << chunkIdx;
                     var mask = chunkMask & value;
 
                     if (mask == 0x0)
                         continue;
 
-                    var relativeTile = SharedNavMapSystem.GetTile(mask);
-                    var tile = (chunk.Origin * SharedNavMapSystem.ChunkSize + relativeTile) * _grid.TileSize;
+                    var relativeTile = GetTileFromIndex(chunkIdx);
+                    var tile = (chunk.Origin * ChunkSize + relativeTile) * _grid.TileSize;
                     tile = tile with { Y = -tile.Y };
 
                     PowerCableChunk neighborChunk;
@@ -212,39 +222,39 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
                     // Note: we only check the north and east neighbors
 
                     // East
-                    if (relativeTile.X == SharedNavMapSystem.ChunkSize - 1)
+                    if (relativeTile.X == ChunkSize - 1)
                     {
                         neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(1, 0), out neighborChunk) &&
-                                    (neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
+                                    (neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(0, relativeTile.Y))) != 0x0;
                     }
                     else
                     {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(1, 0));
+                        var flag = GetFlag(relativeTile + new Vector2i(1, 0));
                         neighbor = (chunkMask & flag) != 0x0;
                     }
 
                     if (neighbor)
                     {
                         // Add points
-                        AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), HorizLinesLookup, HorizLinesLookupReversed, cableIdx);
+                        AddOrUpdateNavMapLine(tile, tile + new Vector2i(_grid.TileSize, 0), horizLines, horizLinesReversed);
                     }
 
                     // North
-                    if (relativeTile.Y == SharedNavMapSystem.ChunkSize - 1)
+                    if (relativeTile.Y == ChunkSize - 1)
                     {
                         neighbor = chunks.TryGetValue(chunkOrigin + new Vector2i(0, 1), out neighborChunk) &&
-                                    (neighborChunk.PowerCableData[cableIdx] & SharedNavMapSystem.GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
+                                    (neighborChunk.PowerCableData[cableIdx] & GetFlag(new Vector2i(relativeTile.X, 0))) != 0x0;
                     }
                     else
                     {
-                        var flag = SharedNavMapSystem.GetFlag(relativeTile + new Vector2i(0, 1));
+                        var flag = GetFlag(relativeTile + new Vector2i(0, 1));
                         neighbor = (chunkMask & flag) != 0x0;
                     }
 
                     if (neighbor)
                     {
                         // Add points
-                        AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, VertLinesLookup, VertLinesLookupReversed, cableIdx);
+                        AddOrUpdateNavMapLine(tile + new Vector2i(0, -_grid.TileSize), tile, vertLines, vertLinesReversed);
                     }
                 }
 
@@ -253,11 +263,25 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
 
         var gridOffset = new Vector2(_grid.TileSize * 0.5f, -_grid.TileSize * 0.5f);
 
-        foreach (var (origin, terminal) in HorizLinesLookup)
-            decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
+        for (var index = 0; index < _horizLines.Length; index++)
+        {
+            var horizLines = _horizLines[index];
+            foreach (var (origin, terminal) in horizLines)
+            {
+                decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
+                    (PowerMonitoringConsoleLineGroup) index));
+            }
+        }
 
-        foreach (var (origin, terminal) in VertLinesLookup)
-            decodedOutput.Add(new PowerMonitoringConsoleLine(origin.Item2 + gridOffset, terminal.Item2 + gridOffset, (PowerMonitoringConsoleLineGroup) origin.Item1));
+        for (var index = 0; index < _vertLines.Length; index++)
+        {
+            var vertLines = _vertLines[index];
+            foreach (var (origin, terminal) in vertLines)
+            {
+                decodedOutput.Add(new PowerMonitoringConsoleLine(origin + gridOffset, terminal + gridOffset,
+                    (PowerMonitoringConsoleLineGroup) index));
+            }
+        }
 
         return decodedOutput;
     }
index 2b322789e2942f62472b5590540d7f9b1e29423e..dba964753f87a7ef58b0f44af3335d158c1245f9 100644 (file)
@@ -12,11 +12,7 @@ using JetBrains.Annotations;
 using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Timing;
-using Robust.Shared.Utility;
 using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using Content.Shared.Atmos;
-using Content.Shared.Doors.Components;
 
 namespace Content.Server.Pinpointer;
 
@@ -44,6 +40,10 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
     {
         base.Initialize();
 
+        var categories = Enum.GetNames(typeof(NavMapChunkType)).Length - 1; // -1 due to "Invalid" entry.
+        if (Categories != categories)
+            throw new Exception($"{nameof(Categories)} must be equal to the number of chunk types");
+
         _airtightQuery = GetEntityQuery<AirtightComponent>();
         _gridQuery = GetEntityQuery<MapGridComponent>();
         _navQuery = GetEntityQuery<NavMapComponent>();
@@ -65,15 +65,12 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
         SubscribeLocalEvent<ConfigurableNavMapBeaconComponent, ExaminedEvent>(OnConfigurableExamined);
     }
 
-    #region: Initialization event handling
     private void OnStationInit(StationGridAddedEvent ev)
     {
         var comp = EnsureComp<NavMapComponent>(ev.GridId);
         RefreshGrid(ev.GridId, comp, Comp<MapGridComponent>(ev.GridId));
     }
 
-    #endregion
-
     #region: Grid change event handling
 
     private void OnNavMapSplit(ref GridSplitEvent args)
@@ -112,16 +109,30 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
         var chunk = EnsureChunk(navMap, chunkOrigin);
 
         // This could be easily replaced in the future to accommodate diagonal tiles
+        var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
+        ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
+
         if (ev.NewTile.IsSpace(_tileDefManager))
-            UnsetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
+        {
+            tileData = 0;
+            if (PruneEmpty((ev.NewTile.GridUid, navMap), chunk))
+                return;
+        }
         else
-            SetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
-
-        if (!PruneEmpty((ev.NewTile.GridUid, navMap), chunk))
         {
-            chunk.LastUpdate = _gameTiming.CurTick;
-            Dirty(ev.NewTile.GridUid, navMap);
+            tileData = FloorMask;
         }
+
+        DirtyChunk((ev.NewTile.GridUid, navMap), chunk);
+    }
+
+    private void DirtyChunk(Entity<NavMapComponent> entity, NavMapChunk chunk)
+    {
+        if (chunk.LastUpdate == _gameTiming.CurTick)
+            return;
+
+        chunk.LastUpdate = _gameTiming.CurTick;
+        Dirty(entity);
     }
 
     private void OnAirtightChange(ref AirtightChanged args)
@@ -137,15 +148,13 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
             return;
         }
 
-        // Refresh the affected tile
         var chunkOrigin = SharedMapSystem.GetChunkIndices(args.Position.Tile, ChunkSize);
+        var (newValue, chunk) = RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, args.Position.Tile, setFloor: false);
 
-        var chunk = RefreshTileEntityContents(gridUid, navMap, mapGrid, chunkOrigin, args.Position.Tile);
-        if (!PruneEmpty((gridUid, navMap), chunk))
-        {
-            chunk.LastUpdate = _gameTiming.CurTick;
-            Dirty(gridUid, navMap);
-        }
+        if (newValue == 0 && PruneEmpty((gridUid, navMap), chunk))
+            return;
+
+        DirtyChunk((gridUid, navMap), chunk);
     }
 
     #endregion
@@ -238,87 +247,63 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
 
             var chunk = EnsureChunk(component, chunkOrigin);
             chunk.LastUpdate = _gameTiming.CurTick;
-
-            // Refresh the floor tile
-            SetAllEdgesForChunkTile(chunk, tile, NavMapChunkType.Floor);
-
-            // Refresh the contents of the tile
-            RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile);
+            RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile, setFloor: true);
         }
 
         Dirty(uid, component);
     }
 
-    private NavMapChunk RefreshTileEntityContents(EntityUid uid, NavMapComponent component, MapGridComponent mapGrid, Vector2i chunkOrigin, Vector2i tile)
+    private (int NewVal, NavMapChunk Chunk) RefreshTileEntityContents(EntityUid uid,
+        NavMapComponent component,
+        MapGridComponent mapGrid,
+        Vector2i chunkOrigin,
+        Vector2i tile,
+        bool setFloor)
     {
         var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
-        var flag = (ushort) GetFlag(relative);
-        var invFlag = (ushort) ~flag;
         var chunk = EnsureChunk(component, chunkOrigin);
+        ref var tileData = ref chunk.TileData[GetTileIndex(relative)];
 
-        // Clear stale data from the tile across all entity associated chunks
-        foreach (var category in EntityChunkTypes)
-        {
-            var data = chunk.EnsureType(category);
-
-            foreach (var direction in data.Keys)
-            {
-                data[direction] &= invFlag;
-            }
-        }
+        // Clear all data except for floor bits
+        if (setFloor)
+            tileData = FloorMask;
+        else
+            tileData &= FloorMask;
 
-        // Update the tile data based on what entities are still anchored to the tile
         var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(uid, mapGrid, tile);
-
         while (enumerator.MoveNext(out var ent))
         {
             if (!_airtightQuery.TryComp(ent, out var airtight))
                 continue;
 
-            var category = GetAssociatedEntityChunkType(ent.Value);
-            var data = chunk.EnsureType(category);
+            var category = GetEntityType(ent.Value);
+            if (category == NavMapChunkType.Invalid)
+                continue;
 
-            foreach (var direction in data.Keys)
-            {
-                if ((direction & airtight.AirBlockedDirection) > 0)
-                {
-                    data[direction] |= flag;
-                }
-            }
+            var directions = (int)airtight.AirBlockedDirection;
+            tileData |= directions << (int) category;
         }
 
         // Remove walls that intersect with doors (unless they can both physically fit on the same tile)
-        var wallData = chunk.TileData[(int) NavMapChunkType.Wall];
-        var airlockData = chunk.TileData[(int) NavMapChunkType.Airlock];
+        // TODO NAVMAP why can this even happen?
+        // Is this for blast-doors or something?
 
-        if (wallData != null && airlockData != null)
-        {
-            foreach (var direction in wallData.Keys)
-            {
-                var airlockInvFlag = (ushort) ~airlockData[direction];
-                wallData[direction] &= airlockInvFlag;
-            }
-        }
+        // Shift airlock bits over to the wall bits
+        var shiftedAirlockBits = (tileData & AirlockMask) >> ((int) NavMapChunkType.Airlock - (int) NavMapChunkType.Wall);
 
-        return chunk;
+        // And then mask door bits
+        tileData &= ~shiftedAirlockBits;
+
+        return (tileData, chunk);
     }
 
     private bool PruneEmpty(Entity<NavMapComponent> entity, NavMapChunk chunk)
     {
-        for (var i = 0; i < NavMapComponent.Categories; i++)
+        foreach (var val in chunk.TileData)
         {
-            var data = chunk.TileData[i];
-
-            if (data == null)
-                continue;
-
-            foreach (var value in data.Values)
-            {
-                if (value != 0)
-                {
-                    return false;
-                }
-            }
+            // TODO NAVMAP SIMD
+            if (val != 0)
+                return false;
         }
 
         entity.Comp.Chunks.Remove(chunk.Origin);
@@ -341,19 +326,12 @@ public sealed partial class NavMapSystem : SharedNavMapSystem
         if (!_navQuery.TryComp(xform.GridUid, out var navMap))
             return;
 
-        var netEnt = GetNetEntity(uid);
-        var oldBeacon = navMap.Beacons.FirstOrNull(x => x.NetEnt == netEnt);
-        var changed = false;
-
-        if (oldBeacon != null)
-        {
-            navMap.Beacons.Remove(oldBeacon.Value);
-            changed = true;
-        }
+        var meta = MetaData(uid);
+        var changed = navMap.Beacons.Remove(meta.NetEntity);
 
-        if (TryCreateNavMapBeaconData(uid, component, xform, out var beaconData))
+        if (TryCreateNavMapBeaconData(uid, component, xform, meta, out var beaconData))
         {
-            navMap.Beacons.Add(beaconData.Value);
+            navMap.Beacons.Add(meta.NetEntity, beaconData.Value);
             changed = true;
         }
 
index be1238fd2b7e770cf37363c19260103ad1de53e2..42c84b7f43b613958432c1fcb9265661c0e6f585 100644 (file)
@@ -1,7 +1,5 @@
-using Content.Server.GameTicking.Rules.Components;
 using Content.Server.NodeContainer;
 using Content.Server.NodeContainer.EntitySystems;
-using Content.Server.NodeContainer.NodeGroups;
 using Content.Server.NodeContainer.Nodes;
 using Content.Server.Power.Components;
 using Content.Server.Power.Nodes;
@@ -13,10 +11,8 @@ using Content.Shared.Power;
 using JetBrains.Annotations;
 using Robust.Server.GameObjects;
 using Robust.Shared.Map.Components;
-using Robust.Shared.Player;
 using Robust.Shared.Utility;
 using System.Linq;
-using System.Diagnostics.CodeAnalysis;
 using Content.Server.GameTicking.Components;
 
 namespace Content.Server.Power.EntitySystems;
@@ -163,7 +159,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
             allChunks = new();
 
         var tile = _sharedMapSystem.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
-        var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, SharedNavMapSystem.ChunkSize);
+        var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
 
         if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
         {
@@ -171,8 +167,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
             allChunks[chunkOrigin] = chunk;
         }
 
-        var relative = SharedMapSystem.GetChunkRelative(tile, SharedNavMapSystem.ChunkSize);
-        var flag = SharedNavMapSystem.GetFlag(relative);
+        var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
+        var flag = GetFlag(relative);
 
         if (args.Anchored)
             chunk.PowerCableData[(int) component.CableType] |= flag;
@@ -884,7 +880,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
                 continue;
 
             var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
-            var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, SharedNavMapSystem.ChunkSize);
+            var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
 
             if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
             {
@@ -892,8 +888,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
                 allChunks[chunkOrigin] = chunk;
             }
 
-            var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, SharedNavMapSystem.ChunkSize);
-            var flag = SharedNavMapSystem.GetFlag(relative);
+            var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
+            var flag = GetFlag(relative);
 
             chunk.PowerCableData[(int) cable.CableType] |= flag;
         }
@@ -910,7 +906,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
             var xform = Transform(ent);
             var tile = _sharedMapSystem.GetTileRef(gridUid, grid, xform.Coordinates);
             var gridIndices = tile.GridIndices;
-            var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, SharedNavMapSystem.ChunkSize);
+            var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, ChunkSize);
 
             if (!component.FocusChunks.TryGetValue(chunkOrigin, out var chunk))
             {
@@ -918,8 +914,8 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori
                 component.FocusChunks[chunkOrigin] = chunk;
             }
 
-            var relative = SharedMapSystem.GetChunkRelative(gridIndices, SharedNavMapSystem.ChunkSize);
-            var flag = SharedNavMapSystem.GetFlag(relative);
+            var relative = SharedMapSystem.GetChunkRelative(gridIndices, ChunkSize);
+            var flag = GetFlag(relative);
 
             if (TryComp<CableComponent>(ent, out var cable))
                 chunk.PowerCableData[(int) cable.CableType] |= flag;
index 09ba521aa96d4499b2e343a90bc893226ce657f8..a8155ef88d7f9361fb6bf73ef57839a11ebb9870 100644 (file)
@@ -104,15 +104,14 @@ namespace Content.Shared.Atmos
         {
             return direction switch
             {
-                AtmosDirection.East => Angle.FromDegrees(90),
-                AtmosDirection.North => Angle.FromDegrees(180),
-                AtmosDirection.West => Angle.FromDegrees(270),
-                AtmosDirection.South => Angle.FromDegrees(0),
-
-                AtmosDirection.NorthEast => Angle.FromDegrees(135),
-                AtmosDirection.NorthWest => Angle.FromDegrees(205),
-                AtmosDirection.SouthWest => Angle.FromDegrees(315),
-                AtmosDirection.SouthEast => Angle.FromDegrees(45),
+                AtmosDirection.South => Angle.Zero,
+                AtmosDirection.East => new Angle(MathHelper.PiOver2),
+                AtmosDirection.North => new Angle(Math.PI),
+                AtmosDirection.West => new Angle(-MathHelper.PiOver2),
+                AtmosDirection.NorthEast => new Angle(Math.PI*3/4),
+                AtmosDirection.NorthWest => new Angle(-Math.PI*3/4),
+                AtmosDirection.SouthWest => new Angle(-MathHelper.PiOver4),
+                AtmosDirection.SouthEast => new Angle(MathHelper.PiOver4),
 
                 _ => throw new ArgumentOutOfRangeException(nameof(direction), $"It was {direction}."),
             };
index 56b9687b31cdcc121548d3fd2852d799e7f1f71f..d77169d32eddd3e9a706c1afb1eba9b9adac8d4d 100644 (file)
@@ -12,8 +12,6 @@ namespace Content.Shared.Pinpointer;
 [RegisterComponent, NetworkedComponent]
 public sealed partial class NavMapComponent : Component
 {
-    public const int Categories = 4;
-
     /*
      * Don't need DataFields as this can be reconstructed
      */
@@ -28,62 +26,37 @@ public sealed partial class NavMapComponent : Component
     /// List of station beacons.
     /// </summary>
     [ViewVariables]
-    public HashSet<SharedNavMapSystem.NavMapBeacon> Beacons = new();
+    public Dictionary<NetEntity, SharedNavMapSystem.NavMapBeacon> Beacons = new();
 }
 
 [Serializable, NetSerializable]
-public sealed class NavMapChunk
+public sealed class NavMapChunk(Vector2i origin)
 {
     /// <summary>
     /// The chunk origin
     /// </summary>
-    public readonly Vector2i Origin;
+    [ViewVariables]
+    public readonly Vector2i Origin = origin;
 
     /// <summary>
-    /// Array with each entry corresponding to a <see cref="NavMapChunkType"/>.
-    /// Uses a bitmask for tiles, 1 for occupied and 0 for empty. There is a bitmask for each cardinal direction,
-    /// representing each edge of the tile, in case the entities inside it do not entirely fill it
+    /// Array containing the chunk's data. The
     /// </summary>
-    public Dictionary<AtmosDirection, ushort>?[] TileData;
+    [ViewVariables]
+    public int[] TileData = new int[SharedNavMapSystem.ArraySize];
 
     /// <summary>
     /// The last game tick that the chunk was updated
     /// </summary>
     [NonSerialized]
     public GameTick LastUpdate;
-
-    public NavMapChunk(Vector2i origin)
-    {
-        Origin = origin;
-        TileData = new Dictionary<AtmosDirection, ushort>?[NavMapComponent.Categories];
-    }
-
-    public Dictionary<AtmosDirection, ushort> EnsureType(NavMapChunkType chunkType)
-    {
-        var data = TileData[(int) chunkType];
-
-        if (data == null)
-        {
-            data = new Dictionary<AtmosDirection, ushort>()
-            {
-                [AtmosDirection.North] = 0,
-                [AtmosDirection.East] = 0,
-                [AtmosDirection.South] = 0,
-                [AtmosDirection.West] = 0,
-            };
-
-            TileData[(int) chunkType] = data;
-        }
-
-        return data;
-    }
 }
 
 public enum NavMapChunkType : byte
 {
-    Invalid,
-    Floor,
-    Wall,
-    Airlock,
-    // Update the categories const if you update this.
+    // Values represent bit shift offsets when retrieving data in the tile array.
+    Invalid  = byte.MaxValue,
+    Floor = 0, // I believe floors have directional information for diagonal tiles?
+    Wall = SharedNavMapSystem.Directions,
+    Airlock = 2 * SharedNavMapSystem.Directions,
 }
+
index 76dfd83e4e2dd84c35b8871ae5cebe7e608e4ddb..0edcd5a4378c419165d995f38fe9b9b6ee51b782 100644 (file)
@@ -1,6 +1,6 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Numerics;
-using Content.Shared.Atmos;
+using System.Runtime.CompilerServices;
 using Content.Shared.Tag;
 using Robust.Shared.GameStates;
 using Robust.Shared.Serialization;
@@ -11,19 +11,22 @@ namespace Content.Shared.Pinpointer;
 
 public abstract class SharedNavMapSystem : EntitySystem
 {
-    [Dependency] private readonly TagSystem _tagSystem = default!;
-    [Dependency] private readonly IGameTiming _gameTiming = default!;
+    public const int Categories = 3;
+    public const int Directions = 4; // Not directly tied to number of atmos directions
 
-    public const byte ChunkSize = 4;
+    public const int ChunkSize = 8;
+    public const int ArraySize = ChunkSize* ChunkSize;
 
-    public readonly NavMapChunkType[] EntityChunkTypes =
-    {
-        NavMapChunkType.Invalid,
-        NavMapChunkType.Wall,
-        NavMapChunkType.Airlock,
-    };
+    public const int AllDirMask = (1 << Directions) - 1;
+    public const int AirlockMask = AllDirMask << (int) NavMapChunkType.Airlock;
+    public const int WallMask = AllDirMask << (int) NavMapChunkType.Wall;
+    public const int FloorMask = AllDirMask << (int) NavMapChunkType.Floor;
+
+    [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!;
+    [Robust.Shared.IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
 
     private readonly string[] _wallTags = ["Wall", "Window"];
+    private EntityQuery<NavMapDoorComponent> _doorQuery;
 
     public override void Initialize()
     {
@@ -31,112 +34,49 @@ public abstract class SharedNavMapSystem : EntitySystem
 
         // Data handling events
         SubscribeLocalEvent<NavMapComponent, ComponentGetState>(OnGetState);
+        _doorQuery = GetEntityQuery<NavMapDoorComponent>();
     }
 
-    /// <summary>
-    /// Converts the chunk's tile into a bitflag for the slot.
-    /// </summary>
-    public static int GetFlag(Vector2i relativeTile)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int GetTileIndex(Vector2i relativeTile)
     {
-        return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
+        return relativeTile.X * ChunkSize + relativeTile.Y;
     }
 
     /// <summary>
-    /// Converts the chunk's tile into a bitflag for the slot.
+    /// Inverse of <see cref="GetTileIndex"/>
     /// </summary>
-    public static Vector2i GetTile(int flag)
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Vector2i GetTileFromIndex(int index)
     {
-        var value = Math.Log2(flag);
-        var x = (int) value / ChunkSize;
-        var y = (int) value % ChunkSize;
-        var result = new Vector2i(x, y);
-
-        DebugTools.Assert(GetFlag(result) == flag);
-
+        var x = index / ChunkSize;
+        var y = index % ChunkSize;
         return new Vector2i(x, y);
     }
 
-    public void SetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile, NavMapChunkType chunkType)
-    {
-        var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
-        var flag = (ushort) GetFlag(relative);
-        var data = chunk.EnsureType(chunkType);
-
-        foreach (var direction in data.Keys)
-        {
-            data[direction] |= flag;
-        }
-    }
-
-    public void UnsetAllEdgesForChunkTile(NavMapChunk chunk, Vector2i tile, NavMapChunkType chunkType)
-    {
-        var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
-        var flag = (ushort) GetFlag(relative);
-        var invFlag = (ushort) ~flag;
-
-        var data = chunk.EnsureType(chunkType);
-
-        foreach (var direction in data.Keys)
-        {
-            data[direction] &= invFlag;
-        }
-    }
-
-    public ushort GetCombinedEdgesForChunk(Dictionary<AtmosDirection, ushort> tile)
-    {
-        ushort combined = 0;
-
-        foreach (var value in tile.Values)
-        {
-            combined |= value;
-        }
-
-        return combined;
-    }
-
-    public bool AllTileEdgesAreOccupied(Dictionary<AtmosDirection, ushort> tileData, Vector2i tile)
-    {
-        var flag = (ushort) GetFlag(tile);
-
-        foreach (var value in tileData.Values)
-        {
-            if ((value & flag) == 0)
-                return false;
-        }
-
-        return true;
-    }
-
-    public NavMapChunkType GetAssociatedEntityChunkType(EntityUid uid)
+    public NavMapChunkType GetEntityType(EntityUid uid)
     {
-        var category = NavMapChunkType.Invalid;
-
-        if (HasComp<NavMapDoorComponent>(uid))
-            category = NavMapChunkType.Airlock;
+        if (_doorQuery.HasComp(uid))
+            return  NavMapChunkType.Airlock;
 
-        else if (_tagSystem.HasAnyTag(uid, _wallTags))
-            category = NavMapChunkType.Wall;
+        if (_tagSystem.HasAnyTag(uid, _wallTags))
+            return NavMapChunkType.Wall;
 
-        return category;
+        return NavMapChunkType.Invalid;
     }
 
-    protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, [NotNullWhen(true)] out NavMapBeacon? beaconData)
+    protected bool TryCreateNavMapBeaconData(EntityUid uid, NavMapBeaconComponent component, TransformComponent xform, MetaDataComponent meta, [NotNullWhen(true)] out NavMapBeacon? beaconData)
     {
         beaconData = null;
 
         if (!component.Enabled || xform.GridUid == null || !xform.Anchored)
             return false;
 
-        string? name = component.Text;
-        var meta = MetaData(uid);
-
+        var name = component.Text;
         if (string.IsNullOrEmpty(name))
             name = meta.EntityName;
 
-        beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition)
-        {
-            LastUpdate = _gameTiming.CurTick
-        };
+        beaconData = new NavMapBeacon(meta.NetEntity, component.Color, name, xform.LocalPosition);
 
         return true;
     }
@@ -145,91 +85,36 @@ public abstract class SharedNavMapSystem : EntitySystem
 
     private void OnGetState(EntityUid uid, NavMapComponent component, ref ComponentGetState args)
     {
-        var chunks = new Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]>();
-        var beacons = new HashSet<NavMapBeacon>();
+        Dictionary<Vector2i, int[]> chunks;
 
         // Should this be a full component state or a delta-state?
         if (args.FromTick <= component.CreationTick)
         {
+            // Full state
+            chunks = new(component.Chunks.Count);
             foreach (var (origin, chunk) in component.Chunks)
             {
-                var sentChunk = new Dictionary<AtmosDirection, ushort>[NavMapComponent.Categories];
-                chunks.Add(origin, sentChunk);
-
-                foreach (var value in Enum.GetValues<NavMapChunkType>())
-                {
-                    ref var data = ref chunk.TileData[(int) value];
-
-                    if (data == null)
-                        continue;
-
-                    var chunkDatum = new Dictionary<AtmosDirection, ushort>(data.Count);
-
-                    foreach (var (direction, tileData) in data)
-                    {
-                        chunkDatum[direction] = tileData;
-                    }
-
-                    sentChunk[(int) value] = chunkDatum;
-                }
-            }
-
-            var beaconQuery = AllEntityQuery<NavMapBeaconComponent, TransformComponent>();
-
-            while (beaconQuery.MoveNext(out var beaconUid, out var beacon, out var xform))
-            {
-                if (xform.GridUid != uid)
-                    continue;
-
-                if (!TryCreateNavMapBeaconData(beaconUid, beacon, xform, out var beaconData))
-                    continue;
-
-                beacons.Add(beaconData.Value);
+                chunks.Add(origin, chunk.TileData);
             }
 
-            args.State = new NavMapComponentState(chunks, beacons);
+            args.State = new NavMapComponentState(chunks, component.Beacons);
             return;
         }
 
+        chunks = new();
         foreach (var (origin, chunk) in component.Chunks)
         {
             if (chunk.LastUpdate < args.FromTick)
                 continue;
 
-            var sentChunk = new Dictionary<AtmosDirection, ushort>[NavMapComponent.Categories];
-            chunks.Add(origin, sentChunk);
-
-            foreach (var value in Enum.GetValues<NavMapChunkType>())
-            {
-                ref var data = ref chunk.TileData[(int) value];
-
-                // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
-                if (data == null)
-                    continue;
-
-                var chunkDatum = new Dictionary<AtmosDirection, ushort>(data.Count);
-
-                foreach (var (direction, tileData) in data)
-                {
-                    chunkDatum[direction] = tileData;
-                }
-
-                sentChunk[(int) value] = chunkDatum;
-            }
-        }
-
-        foreach (var beacon in component.Beacons)
-        {
-            if (beacon.LastUpdate < args.FromTick)
-                continue;
-
-            beacons.Add(beacon);
+            chunks.Add(origin, chunk.TileData);
         }
 
-        args.State = new NavMapComponentState(chunks, beacons)
+        args.State = new NavMapComponentState(chunks, component.Beacons)
         {
+            // TODO NAVMAP cache a single AllChunks hashset in the component.
+            // Or maybe just only send them if a chunk gets removed.
             AllChunks = new(component.Chunks.Keys),
-            AllBeacons = new(component.Beacons)
         };
     }
 
@@ -238,22 +123,18 @@ public abstract class SharedNavMapSystem : EntitySystem
     #region: System messages
 
     [Serializable, NetSerializable]
-    protected sealed class NavMapComponentState : ComponentState, IComponentDeltaState
+    protected sealed class NavMapComponentState(
+        Dictionary<Vector2i, int[]> chunks,
+        Dictionary<NetEntity, NavMapBeacon> beacons)
+        : ComponentState, IComponentDeltaState
     {
-        public Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]> Chunks = new();
-        public HashSet<NavMapBeacon> Beacons = new();
+        public Dictionary<Vector2i, int[]> Chunks = chunks;
+        public Dictionary<NetEntity, NavMapBeacon> Beacons = beacons;
 
         // Required to infer deleted/missing chunks for delta states
         public HashSet<Vector2i>? AllChunks;
-        public HashSet<NavMapBeacon>? AllBeacons;
-
-        public NavMapComponentState(Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]> chunks, HashSet<NavMapBeacon> beacons)
-        {
-            Chunks = chunks;
-            Beacons = beacons;
-        }
 
-        public bool FullState => (AllChunks == null || AllBeacons == null);
+        public bool FullState => AllChunks == null;
 
         public void ApplyToFullState(IComponentState fullState)
         {
@@ -261,32 +142,24 @@ public abstract class SharedNavMapSystem : EntitySystem
             var state = (NavMapComponentState) fullState;
             DebugTools.Assert(state.FullState);
 
-            // Update chunks
             foreach (var key in state.Chunks.Keys)
             {
                 if (!AllChunks!.Contains(key))
                     state.Chunks.Remove(key);
             }
 
-            foreach (var (chunk, data) in Chunks)
+            foreach (var (index, data) in Chunks)
             {
-                for (var i = 0; i < NavMapComponent.Categories; i++)
-                {
-                    var chunkData = data[i];
-                    state.Chunks[chunk][i] = chunkData == null ? chunkData : new(chunkData);
-                }
-            }
+                if (!state.Chunks.TryGetValue(index, out var stateValue))
+                    state.Chunks[index] = stateValue = new int[data.Length];
 
-            // Update beacons
-            foreach (var beacon in state.Beacons)
-            {
-                if (!AllBeacons!.Contains(beacon))
-                    state.Beacons.Remove(beacon);
+                Array.Copy(data, stateValue, data.Length);
             }
 
-            foreach (var beacon in Beacons)
+            state.Beacons.Clear();
+            foreach (var (nuid, beacon) in Beacons)
             {
-                state.Beacons.Add(beacon);
+                state.Beacons.Add(nuid, beacon);
             }
         }
 
@@ -296,56 +169,26 @@ public abstract class SharedNavMapSystem : EntitySystem
             var state = (NavMapComponentState) fullState;
             DebugTools.Assert(state.FullState);
 
-            var chunks = new Dictionary<Vector2i, Dictionary<AtmosDirection, ushort>?[]>();
-            var beacons = new HashSet<NavMapBeacon>();
-
-            foreach (var (chunk, data) in Chunks)
+            var chunks = new Dictionary<Vector2i, int[]>(state.Chunks.Count);
+            foreach (var (index, data) in state.Chunks)
             {
-                for (var i = 0; i < NavMapComponent.Categories; i++)
-                {
-                    var chunkData = data[i];
-                    state.Chunks[chunk][i] = chunkData == null ? chunkData : new(chunkData);
-                }
-            }
-
-            foreach (var (chunk, data) in state.Chunks)
-            {
-                if (AllChunks!.Contains(chunk))
-                {
-                    var copied = new Dictionary<AtmosDirection, ushort>?[NavMapComponent.Categories];
-
-                    for (var i = 0; i < NavMapComponent.Categories; i++)
-                    {
-                        var chunkData = data[i];
-                        copied[i] = chunkData == null ? chunkData : new(chunkData);
-                    }
-
-                    chunks.TryAdd(chunk, copied);
-                }
-            }
+                if (!AllChunks!.Contains(index))
+                    continue;
 
-            foreach (var beacon in Beacons)
-            {
-                beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
-            }
+                var newData = chunks[index] = new int[ArraySize];
 
-            foreach (var beacon in state.Beacons)
-            {
-                if (AllBeacons!.Contains(beacon))
-                {
-                    beacons.Add(new NavMapBeacon(beacon.NetEnt, beacon.Color, beacon.Text, beacon.Position));
-                }
+                if (Chunks.TryGetValue(index, out var updatedData))
+                    Array.Copy(newData, updatedData, ArraySize);
+                else
+                    Array.Copy(newData, data, ArraySize);
             }
 
-            return new NavMapComponentState(chunks, beacons);
+            return new NavMapComponentState(chunks, new(Beacons));
         }
     }
 
     [Serializable, NetSerializable]
-    public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position)
-    {
-        public GameTick LastUpdate;
-    }
+    public record struct NavMapBeacon(NetEntity NetEnt, Color Color, string Text, Vector2 Position);
 
     #endregion
 }
index dc4af23c2390a7c854fc54242c23aaf1a9292284..749f0233aa8d1a606d16f65badabfc2037887b4c 100644 (file)
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using JetBrains.Annotations;
 
 namespace Content.Shared.Power;
@@ -5,4 +6,23 @@ namespace Content.Shared.Power;
 [UsedImplicitly]
 public abstract class SharedPowerMonitoringConsoleSystem : EntitySystem
 {
+    // Chunk size is limited as we require ChunkSize^2 <= 32 (number of bits in an int)
+    public const int ChunkSize = 5;
+
+    /// <summary>
+    /// Converts the chunk's tile into a bitflag for the slot.
+    /// </summary>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static int GetFlag(Vector2i relativeTile)
+    {
+        return 1 << (relativeTile.X * ChunkSize + relativeTile.Y);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static Vector2i GetTileFromIndex(int index)
+    {
+        var x = index / ChunkSize;
+        var y = index % ChunkSize;
+        return new Vector2i(x, y);
+    }
 }
index 39a107b94d4f66e6874b83512e2a6c3e2c547a9d..fdb7de1f757bac332d26fb5c423df3def9618900 100644 (file)
@@ -541,7 +541,15 @@ public sealed class TagSystem : EntitySystem
     /// </exception>
     public bool HasAnyTag(TagComponent component, params string[] ids)
     {
-        return HasAnyTag(component, ids.AsEnumerable());
+        foreach (var id in ids)
+        {
+            AssertValidTag(id);
+
+            if (component.Tags.Contains(id))
+                return true;
+        }
+
+        return false;
     }