]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix biome marker layer command (#21278)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 18 Nov 2023 07:08:20 +0000 (18:08 +1100)
committerGitHub <noreply@github.com>
Sat, 18 Nov 2023 07:08:20 +0000 (18:08 +1100)
Content.Server/Parallax/BiomeSystem.Commands.cs
Content.Server/Parallax/BiomeSystem.cs
Content.Shared/Parallax/Biomes/BiomeComponent.cs

index 279258129242b33ba960d656e1bab52ddedff2c6..0360c1deb7ea2313d91e2ba5b4dca437a5f93efc 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using Content.Server.Administration;
 using Content.Shared.Administration;
 using Content.Shared.Parallax.Biomes;
@@ -5,6 +6,7 @@ using Content.Shared.Parallax.Biomes.Layers;
 using Content.Shared.Parallax.Biomes.Markers;
 using Robust.Shared.Console;
 using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
 
 namespace Content.Server.Parallax;
 
@@ -151,14 +153,27 @@ public sealed partial class BiomeSystem
             return;
         }
 
-        biome.MarkerLayers.Add(args[1]);
+        if (!biome.MarkerLayers.Add(args[1]))
+        {
+            return;
+        }
+
+        biome.ForcedMarkerLayers.Add(args[1]);
     }
 
     private CompletionResult AddMarkerLayerCallbackHelper(IConsoleShell shell, string[] args)
     {
         if (args.Length == 1)
         {
-            return CompletionResult.FromHintOptions(CompletionHelper.Components<BiomeComponent>(args[0], EntityManager), "Biome");
+            var allQuery = AllEntityQuery<MapComponent, BiomeComponent>();
+            var options = new List<CompletionOption>();
+
+            while (allQuery.MoveNext(out var mapComp, out _))
+            {
+                options.Add(new CompletionOption(mapComp.MapId.ToString()));
+            }
+
+            return CompletionResult.FromHintOptions(options, "Biome");
         }
 
         if (args.Length == 2)
index 2687c8a6908332e4054372ec2cdb6fa7bdec57e9..9a2357e7088849612ab6f60c0fcc035e1cfc4a60 100644 (file)
@@ -44,6 +44,8 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
     [Dependency] private readonly SharedMapSystem _mapSystem = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
 
+    private EntityQuery<TransformComponent> _xformQuery;
+
     private readonly HashSet<EntityUid> _handledEntities = new();
     private const float DefaultLoadRange = 16f;
     private float _loadRange = DefaultLoadRange;
@@ -68,6 +70,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
     {
         base.Initialize();
         Log.Level = LogLevel.Debug;
+        _xformQuery = GetEntityQuery<TransformComponent>();
         SubscribeLocalEvent<BiomeComponent, MapInitEvent>(OnBiomeMapInit);
         SubscribeLocalEvent<FTLStartedEvent>(OnFTLStarted);
         SubscribeLocalEvent<ShuttleFlattenEvent>(OnShuttleFlatten);
@@ -393,6 +396,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
                     return;
 
                 // Get the set of spawned nodes to avoid overlap.
+                var forced = component.ForcedMarkerLayers.Contains(layer);
                 var spawnSet = _tilePool.Get();
                 var frontier = new ValueList<Vector2i>(32);
 
@@ -460,8 +464,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
 
                         // Check if it's a valid spawn, if so then use it.
                         var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, node);
+                        enumerator.MoveNext(out var existing);
 
-                        if (enumerator.MoveNext(out _))
+                        if (!forced && existing != null)
                             continue;
 
                         // Check if mask matches // anything blocking.
@@ -499,6 +504,15 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
                         layerMarkers.Add(node);
                         groupSize--;
                         spawnSet.Add(node);
+
+                        if (forced && existing != null)
+                        {
+                            // Just lock anything so we can dump this
+                            lock (component.PendingMarkers)
+                            {
+                                Del(existing.Value);
+                            }
+                        }
                     }
                 }
 
@@ -531,79 +545,97 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
             });
         }
 
+        component.ForcedMarkerLayers.Clear();
         var active = _activeChunks[component];
         List<(Vector2i, Tile)>? tiles = null;
 
         foreach (var chunk in active)
         {
+            LoadMarkerChunk(component, gridUid, grid, chunk, seed);
+
             if (!component.LoadedChunks.Add(chunk))
                 continue;
 
             tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize);
             // Load NOW!
-            LoadChunk(component, gridUid, grid, chunk, seed, tiles, xformQuery);
+            LoadChunk(component, gridUid, grid, chunk, seed, tiles);
         }
     }
 
-    /// <summary>
-    /// Loads a particular queued chunk for a biome.
-    /// </summary>
-    private void LoadChunk(
+    private void LoadMarkerChunk(
         BiomeComponent component,
         EntityUid gridUid,
         MapGridComponent grid,
         Vector2i chunk,
-        int seed,
-        List<(Vector2i, Tile)> tiles,
-        EntityQuery<TransformComponent> xformQuery)
+        int seed)
     {
+        // Load any pending marker tiles first.
+        if (!component.PendingMarkers.TryGetValue(chunk, out var layers))
+            return;
+
+        // This needs to be done separately in case we try to add a marker layer and want to force it on existing
+        // loaded chunks.
         component.ModifiedTiles.TryGetValue(chunk, out var modified);
         modified ??= _tilePool.Get();
 
-        // Load any pending marker tiles first.
-        if (component.PendingMarkers.TryGetValue(chunk, out var layers))
+        foreach (var (layer, nodes) in layers)
         {
-            foreach (var (layer, nodes) in layers)
+            var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
+
+            foreach (var node in nodes)
             {
-                var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
+                if (modified.Contains(node))
+                    continue;
 
-                foreach (var node in nodes)
+                // Need to ensure the tile under it has loaded for anchoring.
+                if (TryGetBiomeTile(node, component.Layers, seed, grid, out var tile))
                 {
-                    if (modified.Contains(node))
-                        continue;
-
-                    // Need to ensure the tile under it has loaded for anchoring.
-                    if (TryGetBiomeTile(node, component.Layers, seed, grid, out var tile))
-                    {
-                        _mapSystem.SetTile(gridUid, grid, node, tile.Value);
-                    }
+                    _mapSystem.SetTile(gridUid, grid, node, tile.Value);
+                }
 
-                    string? prototype;
+                string? prototype;
 
-                    if (TryGetEntity(node, component, grid, out var proto) &&
-                        layerProto.EntityMask.TryGetValue(proto, out var maskedProto))
-                    {
-                        prototype = maskedProto;
-                    }
-                    else
-                    {
-                        prototype = layerProto.Prototype;
-                    }
-
-                    // If it is a ghost role then purge it
-                    // TODO: This is *kind* of a bandaid but natural mobs spawns needs a lot more work.
-                    // Ideally we'd just have ghost role and non-ghost role variants for some stuff.
-                    var uid = EntityManager.CreateEntityUninitialized(prototype, _mapSystem.GridTileToLocal(gridUid, grid, node));
-                    RemComp<GhostTakeoverAvailableComponent>(uid);
-                    RemComp<GhostRoleComponent>(uid);
-                    EntityManager.InitializeAndStartEntity(uid);
-                    modified.Add(node);
+                if (TryGetEntity(node, component, grid, out var proto) &&
+                    layerProto.EntityMask.TryGetValue(proto, out var maskedProto))
+                {
+                    prototype = maskedProto;
+                }
+                else
+                {
+                    prototype = layerProto.Prototype;
                 }
-            }
 
-            component.PendingMarkers.Remove(chunk);
+                // If it is a ghost role then purge it
+                // TODO: This is *kind* of a bandaid but natural mobs spawns needs a lot more work.
+                // Ideally we'd just have ghost role and non-ghost role variants for some stuff.
+                var uid = EntityManager.CreateEntityUninitialized(prototype, _mapSystem.GridTileToLocal(gridUid, grid, node));
+                RemComp<GhostTakeoverAvailableComponent>(uid);
+                RemComp<GhostRoleComponent>(uid);
+                EntityManager.InitializeAndStartEntity(uid);
+                modified.Add(node);
+            }
         }
 
+        if (modified.Count == 0)
+            _tilePool.Return(modified);
+
+        component.PendingMarkers.Remove(chunk);
+    }
+
+    /// <summary>
+    /// Loads a particular queued chunk for a biome.
+    /// </summary>
+    private void LoadChunk(
+        BiomeComponent component,
+        EntityUid gridUid,
+        MapGridComponent grid,
+        Vector2i chunk,
+        int seed,
+        List<(Vector2i, Tile)> tiles)
+    {
+        component.ModifiedTiles.TryGetValue(chunk, out var modified);
+        modified ??= _tilePool.Get();
+
         // Set tiles first
         for (var x = 0; x < ChunkSize; x++)
         {
@@ -653,7 +685,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
                 var ent = Spawn(entPrototype, _mapSystem.GridTileToLocal(gridUid, grid, indices));
 
                 // At least for now unless we do lookups or smth, only work with anchoring.
-                if (xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored)
+                if (_xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored)
                 {
                     _transform.AnchorEntity(ent, xform, gridUid, grid, indices);
                 }
index 498619a17dd73e0869c35b00a29954ca962f8f12..18aa55312c29fc9e621b967f0a646a0540341b03 100644 (file)
@@ -2,6 +2,7 @@ using Content.Shared.Parallax.Biomes.Layers;
 using Content.Shared.Parallax.Biomes.Markers;
 using Robust.Shared.GameStates;
 using Robust.Shared.Noise;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
@@ -68,8 +69,14 @@ public sealed partial class BiomeComponent : Component
     [DataField("loadedMarkers", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<HashSet<Vector2i>, BiomeMarkerLayerPrototype>))]
     public Dictionary<string, HashSet<Vector2i>> LoadedMarkers = new();
 
-    [DataField("markerLayers", customTypeSerializer: typeof(PrototypeIdListSerializer<BiomeMarkerLayerPrototype>))]
-    public List<string> MarkerLayers = new();
+    [DataField]
+    public HashSet<ProtoId<BiomeMarkerLayerPrototype>> MarkerLayers = new();
+
+    /// <summary>
+    /// One-tick forcing of marker layers to bulldoze any entities in the way.
+    /// </summary>
+    [DataField]
+    public HashSet<ProtoId<BiomeMarkerLayerPrototype>> ForcedMarkerLayers = new();
 
     #endregion
 }