Fix biome marker layer command (#21278)

This commit is contained in:
metalgearsloth
2023-11-18 18:08:20 +11:00
committed by GitHub
parent 69af567269
commit 1fa5aaf92e
3 changed files with 107 additions and 53 deletions

View 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)

View 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,20 +545,83 @@ 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);
}
}
private void LoadMarkerChunk(
BiomeComponent component,
EntityUid gridUid,
MapGridComponent grid,
Vector2i chunk,
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();
foreach (var (layer, nodes) in layers)
{
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
foreach (var node in nodes)
{
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);
}
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 (modified.Count == 0)
_tilePool.Return(modified);
component.PendingMarkers.Remove(chunk);
}
/// <summary>
/// Loads a particular queued chunk for a biome.
/// </summary>
@@ -554,56 +631,11 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
MapGridComponent grid,
Vector2i chunk,
int seed,
List<(Vector2i, Tile)> tiles,
EntityQuery<TransformComponent> xformQuery)
List<(Vector2i, Tile)> tiles)
{
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)
{
var layerProto = ProtoManager.Index<BiomeMarkerLayerPrototype>(layer);
foreach (var node in nodes)
{
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);
}
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);
}
}
component.PendingMarkers.Remove(chunk);
}
// 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);
}

View 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
}