Skip to content

Commit

Permalink
Merge branch 'new-frontiers-14:master' into Lockers-Update-2
Browse files Browse the repository at this point in the history
  • Loading branch information
dvir001 authored Aug 22, 2023
2 parents 886ea81 + 9d7338b commit c0530c5
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 2 deletions.
1 change: 1 addition & 0 deletions Content.Client/Entry/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public override void Init()
_prototypeManager.RegisterIgnore("noiseChannel");
_prototypeManager.RegisterIgnore("spaceBiome");
_prototypeManager.RegisterIgnore("worldgenConfig");
_prototypeManager.RegisterIgnore("gcQueue");
_prototypeManager.RegisterIgnore("gameRule");
_prototypeManager.RegisterIgnore("worldSpell");
_prototypeManager.RegisterIgnore("entitySpell");
Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Atmos/EntitySystems/AirtightSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private void OnAirtightShutdown(EntityUid uid, AirtightComponent airtight, Compo

private void OnAirtightPositionChanged(EntityUid uid, AirtightComponent airtight, ref AnchorStateChangedEvent args)
{
var xform = args.Transform;
var xform = Transform(uid);

if (!TryComp(xform.GridUid, out MapGridComponent? grid))
return;
Expand Down
2 changes: 1 addition & 1 deletion Content.Server/Procedural/DungeonJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public DungeonJob(
await PostGen(dordor, dungeon, _gridUid, _grid, random);
break;
case DungeonEntrancePostGen entrance:
await PostGen(entrance, dungeon, _gridUid, _grid, random);
// await PostGen(entrance, dungeon, _gridUid, _grid, random);
break;
case ExternalWindowPostGen externalWindow:
await PostGen(externalWindow, dungeon, _gridUid, _grid, random);
Expand Down
21 changes: 21 additions & 0 deletions Content.Server/Worldgen/Components/GC/GCAbleObjectComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Content.Server.Worldgen.Prototypes;
using Content.Server.Worldgen.Systems.GC;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server.Worldgen.Components.GC;

/// <summary>
/// This is used for whether or not a GCable object is "dirty". Firing GCDirtyEvent on the object is the correct way to
/// set this up.
/// </summary>
[RegisterComponent]
[Access(typeof(GCQueueSystem))]
public sealed class GCAbleObjectComponent : Component
{
/// <summary>
/// Which queue to insert this object into when GCing
/// </summary>
[DataField("queue", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<GCQueuePrototype>))]
public string Queue = default!;
}

41 changes: 41 additions & 0 deletions Content.Server/Worldgen/Prototypes/GCQueuePrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Robust.Shared.Prototypes;

namespace Content.Server.Worldgen.Prototypes;

/// <summary>
/// This is a prototype for a GC queue.
/// </summary>
[Prototype("gcQueue")]
public sealed class GCQueuePrototype : IPrototype
{
/// <inheritdoc />
[IdDataField]
public string ID { get; } = default!;

/// <summary>
/// How deep the GC queue is at most. If this value is ever exceeded entities get processed automatically regardless of
/// tick-time cap.
/// </summary>
[DataField("depth", required: true)]
public int Depth { get; }

/// <summary>
/// The maximum amount of time that can be spent processing this queue.
/// </summary>
[DataField("maximumTickTime")]
public TimeSpan MaximumTickTime { get; } = TimeSpan.FromMilliseconds(1);

/// <summary>
/// The minimum depth before entities in the queue actually get processed for deletion.
/// </summary>
[DataField("minDepthToProcess", required: true)]
public int MinDepthToProcess { get; }

/// <summary>
/// Whether or not the GC should fire an event on the entity to see if it's eligible to skip the queue.
/// Useful for making it so only objects a player has actually interacted with get put in the collection queue.
/// </summary>
[DataField("trySkipQueue")]
public bool TrySkipQueue { get; }
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Numerics;
using Content.Server.Worldgen.Components;
using Content.Server.Worldgen.Components.Debris;
using Content.Server.Worldgen.Systems.GC;
using Content.Server.Worldgen.Tools;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
Expand All @@ -16,6 +17,7 @@ namespace Content.Server.Worldgen.Systems.Debris;
/// </summary>
public sealed class DebrisFeaturePlacerSystem : BaseWorldSystem
{
[Dependency] private readonly GCQueueSystem _gc = default!;
[Dependency] private readonly NoiseIndexSystem _noiseIndex = default!;
[Dependency] private readonly PoissonDiskSampler _sampler = default!;
[Dependency] private readonly TransformSystem _xformSys = default!;
Expand All @@ -33,10 +35,19 @@ public override void Initialize()
SubscribeLocalEvent<DebrisFeaturePlacerControllerComponent, WorldChunkUnloadedEvent>(OnChunkUnloaded);
SubscribeLocalEvent<OwnedDebrisComponent, ComponentShutdown>(OnDebrisShutdown);
SubscribeLocalEvent<OwnedDebrisComponent, MoveEvent>(OnDebrisMove);
SubscribeLocalEvent<OwnedDebrisComponent, TryCancelGC>(OnTryCancelGC);
SubscribeLocalEvent<SimpleDebrisSelectorComponent, TryGetPlaceableDebrisFeatureEvent>(
OnTryGetPlacableDebrisEvent);
}

/// <summary>
/// Handles GC cancellation in case the chunk is still loaded.
/// </summary>
private void OnTryCancelGC(EntityUid uid, OwnedDebrisComponent component, ref TryCancelGC args)
{
args.Cancelled |= HasComp<LoadedChunkComponent>(component.OwningController);
}

/// <summary>
/// Handles debris moving, and making sure it stays parented to a chunk for loading purposes.
/// </summary>
Expand Down Expand Up @@ -91,6 +102,12 @@ private void OnDebrisShutdown(EntityUid uid, OwnedDebrisComponent component, Com
private void OnChunkUnloaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component,
ref WorldChunkUnloadedEvent args)
{
foreach (var (_, debris) in component.OwnedDebris)
{
if (debris is not null)
_gc.TryGCEntity(debris.Value); // gonb.
}

component.DoSpawns = true;
}

Expand Down
124 changes: 124 additions & 0 deletions Content.Server/Worldgen/Systems/GC/GCQueueSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System.Linq;
using Content.Server.Worldgen.Components.GC;
using Content.Server.Worldgen.Prototypes;
using Content.Shared.CCVar;
using JetBrains.Annotations;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.Worldgen.Systems.GC;

/// <summary>
/// This handles delayed garbage collection of entities, to avoid overloading the tick in particularly expensive cases.
/// </summary>
public sealed class GCQueueSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;

[ViewVariables] private TimeSpan _maximumProcessTime = TimeSpan.Zero;

[ViewVariables] private readonly Dictionary<string, Queue<EntityUid>> _queues = new();

/// <inheritdoc />
public override void Initialize()
{
_cfg.OnValueChanged(CCVars.GCMaximumTimeMs, s => _maximumProcessTime = TimeSpan.FromMilliseconds(s),
true);
}

/// <inheritdoc />CCVars
public override void Update(float frameTime)
{
var overallWatch = new Stopwatch();
var queueWatch = new Stopwatch();
var queues = _queues.ToList();
_random.Shuffle(queues); // Avert resource starvation by always processing in random order.
overallWatch.Start();
foreach (var (pId, queue) in queues)
{
if (overallWatch.Elapsed > _maximumProcessTime)
return;

var proto = _proto.Index<GCQueuePrototype>(pId);
if (queue.Count < proto.MinDepthToProcess)
continue;

queueWatch.Restart();
while (queueWatch.Elapsed < proto.MaximumTickTime && queue.Count >= proto.MinDepthToProcess &&
overallWatch.Elapsed < _maximumProcessTime)
{
var e = queue.Dequeue();
if (!Deleted(e))
{
var ev = new TryCancelGC();
RaiseLocalEvent(e, ref ev);

if (!ev.Cancelled)
Del(e);
}
}
}
}

/// <summary>
/// Attempts to GC an entity. This functions as QueueDel if it can't.
/// </summary>
/// <param name="e">Entity to GC.</param>
public void TryGCEntity(EntityUid e)
{
if (!TryComp<GCAbleObjectComponent>(e, out var comp))
{
QueueDel(e); // not our problem :)
return;
}

if (!_queues.TryGetValue(comp.Queue, out var queue))
{
queue = new Queue<EntityUid>();
_queues[comp.Queue] = queue;
}

var proto = _proto.Index<GCQueuePrototype>(comp.Queue);
if (queue.Count > proto.Depth)
{
QueueDel(e); // whelp, too full.
return;
}

if (proto.TrySkipQueue)
{
var ev = new TryGCImmediately();
RaiseLocalEvent(e, ref ev);
if (!ev.Cancelled)
{
QueueDel(e);
return;
}
}

queue.Enqueue(e);
}
}

/// <summary>
/// Fired by GCQueueSystem to check if it can simply immediately GC an entity, for example if it was never fully
/// loaded.
/// </summary>
/// <param name="Cancelled">Whether or not the immediate deletion attempt was cancelled.</param>
[ByRefEvent]
[PublicAPI]
public record struct TryGCImmediately(bool Cancelled = false);

/// <summary>
/// Fired by GCQueueSystem to check if the collection of the given entity should be cancelled, for example it's chunk
/// being loaded again.
/// </summary>
/// <param name="Cancelled">Whether or not the deletion attempt was cancelled.</param>
[ByRefEvent]
[PublicAPI]
public record struct TryCancelGC(bool Cancelled = false);

19 changes: 19 additions & 0 deletions Resources/Changelog/Changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,22 @@ Entries:
message: Disables chasms spawning on expeditions
id: 34
time: '2023-08-19T07:10:23.0000000+00:00'
- author: DebugOk
changes:
- type: Fix
message: Stop demon tiles from spawning
id: 35
time: '2023-08-21T01:39:41.0000000+00:00'
- author: dvir01
changes:
- type: Add
message: >-
Nanotrasen has provided all Frontier security personal with bodycams,
allowing them to stream their heroic actions and dependently not to spy
on them to investigate abuse reports.
- type: Add
message: >-
The new security bodycam channel is available on the wireless camera
monitors.
id: 36
time: '2023-08-22T06:00:24.0000000+00:00'
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/World/Debris/asteroids.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
- id: WallRockArtifactFragment
prob: 0.01
orGroup: rock
- type: GCAbleObject
queue: SpaceDebris
- type: IFF
flags: HideLabel
color: "#d67e27"
Expand Down
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/World/Debris/wrecks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
prob: 0.03
- id: SpawnMobPurpleSnake
prob: 0.02
- type: GCAbleObject
queue: SpaceDebris
- type: IFF
flags: HideLabel
color: "#88b0d1"
Expand Down
4 changes: 4 additions & 0 deletions Resources/Prototypes/GC/world.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- type: gcQueue
id: SpaceDebris
depth: 512 # So there's a decent bit of time before roids unload.
minDepthToProcess: 256

0 comments on commit c0530c5

Please sign in to comment.