From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Date: Mon, 3 Nov 2025 11:49:36 +0000 (-0800)
Subject: Document Atmospherics ExcitedGroups (#41269)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=e6bbd40e724bf4516d62b839a1b6dc6168b244dd;p=space-station-14.git
Document Atmospherics ExcitedGroups (#41269)
Document ExcitedGroups
---
diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs
index 0d622f3067..6ae251dd29 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs
@@ -4,148 +4,199 @@ using Content.Shared.Atmos.Components;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
-namespace Content.Server.Atmos.EntitySystems
+namespace Content.Server.Atmos.EntitySystems;
+
+public sealed partial class AtmosphereSystem
{
- public sealed partial class AtmosphereSystem
+ /*
+ Handles Excited Groups, an optimization routine executed during LINDA
+ that groups active tiles together.
+
+ Groups of active tiles that have very low mole deltas between them
+ are dissolved after a cooldown period, performing a final equalization
+ on all tiles in the group before deactivating them.
+
+ If tiles are so close together in pressure that the final equalization
+ would result in negligible gas transfer, the group is dissolved without
+ performing an equalization.
+
+ This prevents LINDA from constantly transferring tiny amounts of gas
+ between tiles that are already nearly equalized.
+ */
+
+ ///
+ /// Adds a tile to an , resetting the group's cooldowns in the process.
+ ///
+ /// The to add the tile to.
+ /// The to add.
+ private void ExcitedGroupAddTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
{
- private void ExcitedGroupAddTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
- {
- DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
- DebugTools.Assert(tile.ExcitedGroup == null, "Tried to add a tile to an excited group when it's already in another one!");
- excitedGroup.Tiles.Add(tile);
- tile.ExcitedGroup = excitedGroup;
- ExcitedGroupResetCooldowns(excitedGroup);
- }
+ DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
+ DebugTools.Assert(tile.ExcitedGroup == null, "Tried to add a tile to an excited group when it's already in another one!");
+ excitedGroup.Tiles.Add(tile);
+ tile.ExcitedGroup = excitedGroup;
+ ExcitedGroupResetCooldowns(excitedGroup);
+ }
- private void ExcitedGroupRemoveTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
- {
- DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
- DebugTools.Assert(tile.ExcitedGroup == excitedGroup, "Tried to remove a tile from an excited group it's not present in!");
- tile.ExcitedGroup = null;
- excitedGroup.Tiles.Remove(tile);
- }
+ ///
+ /// Removes a tile from an .
+ ///
+ /// The to remove the tile from.
+ /// The to remove.
+ private void ExcitedGroupRemoveTile(ExcitedGroup excitedGroup, TileAtmosphere tile)
+ {
+ DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
+ DebugTools.Assert(tile.ExcitedGroup == excitedGroup, "Tried to remove a tile from an excited group it's not present in!");
+ tile.ExcitedGroup = null;
+ excitedGroup.Tiles.Remove(tile);
+ }
- private void ExcitedGroupMerge(GridAtmosphereComponent gridAtmosphere, ExcitedGroup ourGroup, ExcitedGroup otherGroup)
+ ///
+ /// Merges two , transferring all tiles from one to the other.
+ /// The larger group receives the tiles of the smaller group.
+ /// The smaller group is then disposed of without deactivating its tiles.
+ ///
+ /// The of the grid.
+ /// The first to merge.
+ /// The second to merge.
+ private void ExcitedGroupMerge(GridAtmosphereComponent gridAtmosphere, ExcitedGroup ourGroup, ExcitedGroup otherGroup)
+ {
+ DebugTools.Assert(!ourGroup.Disposed, "Excited group is disposed!");
+ DebugTools.Assert(!otherGroup.Disposed, "Excited group is disposed!");
+ DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(ourGroup), "Grid Atmosphere does not contain Excited Group!");
+ DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(otherGroup), "Grid Atmosphere does not contain Excited Group!");
+ var ourSize = ourGroup.Tiles.Count;
+ var otherSize = otherGroup.Tiles.Count;
+
+ ExcitedGroup winner;
+ ExcitedGroup loser;
+
+ if (ourSize > otherSize)
{
- DebugTools.Assert(!ourGroup.Disposed, "Excited group is disposed!");
- DebugTools.Assert(!otherGroup.Disposed, "Excited group is disposed!");
- DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(ourGroup), "Grid Atmosphere does not contain Excited Group!");
- DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(otherGroup), "Grid Atmosphere does not contain Excited Group!");
- var ourSize = ourGroup.Tiles.Count;
- var otherSize = otherGroup.Tiles.Count;
-
- ExcitedGroup winner;
- ExcitedGroup loser;
-
- if (ourSize > otherSize)
- {
- winner = ourGroup;
- loser = otherGroup;
- }
- else
- {
- winner = otherGroup;
- loser = ourGroup;
- }
-
- foreach (var tile in loser.Tiles)
- {
- tile.ExcitedGroup = winner;
- winner.Tiles.Add(tile);
- }
-
- loser.Tiles.Clear();
- ExcitedGroupDispose(gridAtmosphere, loser);
- ExcitedGroupResetCooldowns(winner);
+ winner = ourGroup;
+ loser = otherGroup;
}
-
- private void ExcitedGroupResetCooldowns(ExcitedGroup excitedGroup)
+ else
{
- DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
- excitedGroup.BreakdownCooldown = 0;
- excitedGroup.DismantleCooldown = 0;
+ winner = otherGroup;
+ loser = ourGroup;
}
- private void ExcitedGroupSelfBreakdown(
- Entity ent,
- ExcitedGroup excitedGroup)
+ foreach (var tile in loser.Tiles)
{
- DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
- DebugTools.Assert(ent.Comp1.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
- var combined = new GasMixture(Atmospherics.CellVolume);
-
- var tileSize = excitedGroup.Tiles.Count;
+ tile.ExcitedGroup = winner;
+ winner.Tiles.Add(tile);
+ }
- if (excitedGroup.Disposed)
- return;
+ loser.Tiles.Clear();
+ ExcitedGroupDispose(gridAtmosphere, loser);
+ ExcitedGroupResetCooldowns(winner);
+ }
- if (tileSize == 0)
- {
- ExcitedGroupDispose(ent.Comp1, excitedGroup);
- return;
- }
+ ///
+ /// Resets the cooldowns of an excited group.
+ ///
+ /// The to reset cooldowns for.
+ private void ExcitedGroupResetCooldowns(ExcitedGroup excitedGroup)
+ {
+ DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
+ excitedGroup.BreakdownCooldown = 0;
+ excitedGroup.DismantleCooldown = 0;
+ }
- foreach (var tile in excitedGroup.Tiles)
- {
- if (tile?.Air == null)
- continue;
+ ///
+ /// Performs a final equalization on all tiles in an excited group before deactivating it.
+ ///
+ /// The grid.
+ /// The to equalize and dissolve.
+ private void ExcitedGroupSelfBreakdown(
+ Entity ent,
+ ExcitedGroup excitedGroup)
+ {
+ DebugTools.Assert(!excitedGroup.Disposed, "Excited group is disposed!");
+ DebugTools.Assert(ent.Comp1.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
+ var combined = new GasMixture(Atmospherics.CellVolume);
- Merge(combined, tile.Air);
+ var tileSize = excitedGroup.Tiles.Count;
- if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Space)
- continue;
+ if (excitedGroup.Disposed)
+ return;
- combined.Clear();
- break;
- }
+ if (tileSize == 0)
+ {
+ ExcitedGroupDispose(ent.Comp1, excitedGroup);
+ return;
+ }
- combined.Multiply(1 / (float)tileSize);
+ // Combine all gasses in the group into a single mixture
+ // for distribution into each individual tile.
+ foreach (var tile in excitedGroup.Tiles)
+ {
+ if (tile?.Air == null)
+ continue;
- foreach (var tile in excitedGroup.Tiles)
- {
- if (tile?.Air == null)
- continue;
+ Merge(combined, tile.Air);
- tile.Air.CopyFrom(combined);
- InvalidateVisuals(ent, tile);
- }
+ // If this tile is space and space is all-consuming, the final equalization
+ // will result in a vacuum, so we can skip the rest of the equalization.
+ if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Space)
+ continue;
- excitedGroup.BreakdownCooldown = 0;
+ combined.Clear();
+ break;
}
- ///
- /// This de-activates and removes all tiles in an excited group.
- ///
- private void DeactivateGroupTiles(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
+ combined.Multiply(1 / (float)tileSize);
+
+ // Distribute the combined mixture evenly to all tiles in the group.
+ foreach (var tile in excitedGroup.Tiles)
{
- foreach (var tile in excitedGroup.Tiles)
- {
- tile.ExcitedGroup = null;
- RemoveActiveTile(gridAtmosphere, tile);
- }
+ if (tile?.Air == null)
+ continue;
- excitedGroup.Tiles.Clear();
+ tile.Air.CopyFrom(combined);
+ InvalidateVisuals(ent, tile);
}
- ///
- /// This removes an excited group without de-activating its tiles.
- ///
- private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
+ excitedGroup.BreakdownCooldown = 0;
+ }
+
+ ///
+ /// Deactivates and removes all tiles from an excited group without performing a final equalization.
+ /// Used when an excited group is expected to be nearly equalized already to avoid unnecessary processing.
+ ///
+ /// The of the grid.
+ /// The to dissolve.
+ private void DeactivateGroupTiles(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
+ {
+ foreach (var tile in excitedGroup.Tiles)
{
- if (excitedGroup.Disposed)
- return;
+ tile.ExcitedGroup = null;
+ RemoveActiveTile(gridAtmosphere, tile);
+ }
+
+ excitedGroup.Tiles.Clear();
+ }
- DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
+ ///
+ /// Removes and disposes of an excited group without performing any final equalization
+ /// or deactivation of its tiles.
+ ///
+ private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
+ {
+ if (excitedGroup.Disposed)
+ return;
- excitedGroup.Disposed = true;
- gridAtmosphere.ExcitedGroups.Remove(excitedGroup);
+ DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
- foreach (var tile in excitedGroup.Tiles)
- {
- tile.ExcitedGroup = null;
- }
+ excitedGroup.Disposed = true;
+ gridAtmosphere.ExcitedGroups.Remove(excitedGroup);
- excitedGroup.Tiles.Clear();
+ foreach (var tile in excitedGroup.Tiles)
+ {
+ tile.ExcitedGroup = null;
}
+
+ excitedGroup.Tiles.Clear();
}
}
diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs
index 55b38924c0..ad19770bfe 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs
@@ -129,9 +129,16 @@ namespace Content.Server.Atmos.EntitySystems
switch (tile.LastShare)
{
+ // Refresh this tile's suspension cooldown if it had significant sharing.
case > Atmospherics.MinimumAirToSuspend:
ExcitedGroupResetCooldowns(tile.ExcitedGroup);
break;
+
+ // If this tile moved a very small amount of air, but not enough to matter,
+ // we set the dismantle cooldown to 0.
+ // This dissolves the group without performing an equalization as we expect
+ // the group to be mostly equalized already if we're moving around miniscule
+ // amounts of air.
case > Atmospherics.MinimumMolesDeltaToMove:
tile.ExcitedGroup.DismantleCooldown = 0;
break;
diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs
index 72e4b5f151..c0f081f9ba 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs
@@ -365,7 +365,6 @@ namespace Content.Server.Atmos.EntitySystems
ExcitedGroupSelfBreakdown(ent, excitedGroup);
else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
DeactivateGroupTiles(gridAtmosphere, excitedGroup);
- // TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it?
if (number++ < LagCheckIterations)
continue;
diff --git a/Content.Server/Atmos/ExcitedGroup.cs b/Content.Server/Atmos/ExcitedGroup.cs
index 73dc6c2329..1554318f65 100644
--- a/Content.Server/Atmos/ExcitedGroup.cs
+++ b/Content.Server/Atmos/ExcitedGroup.cs
@@ -1,13 +1,44 @@
-namespace Content.Server.Atmos
+namespace Content.Server.Atmos;
+
+///
+/// Internal Atmospherics class that stores data about a group of s
+/// that are excited and need to be processed.
+///
+/// Excited Groups is an optimization routine executed during LINDA
+/// that bunches small groups of active s
+/// together and performs equalization processing on the entire group when the group dissolves.
+/// Dissolution happens when LINDA operations between the tiles decrease to very low mole deltas.
+///
+public sealed class ExcitedGroup
{
- public sealed class ExcitedGroup
- {
- [ViewVariables] public bool Disposed = false;
+ ///
+ /// Whether this Active Group has been disposed of.
+ /// Used to make sure we don't perform operations on active groups that
+ /// we've already dissolved.
+ ///
+ [ViewVariables]
+ public bool Disposed = false;
- [ViewVariables] public readonly List Tiles = new(100);
+ ///
+ /// List of tiles that belong to this excited group.
+ ///
+ [ViewVariables]
+ public readonly List Tiles = new(100);
- [ViewVariables] public int DismantleCooldown { get; set; } = 0;
+ ///
+ /// Cycles before this excited group will be queued for dismantling.
+ /// Dismantling is the process of equalizing the atmosphere
+ /// across all tiles in the excited group and removing the group.
+ ///
+ [ViewVariables]
+ public int DismantleCooldown = 0;
- [ViewVariables] public int BreakdownCooldown { get; set; } = 0;
- }
+ ///
+ /// Cycles before this excited group will be allowed to break down and deactivate.
+ /// Breakdown occurs when the excited group is small enough and inactive enough
+ /// to be safely removed without equalization. Used where the mole deltas across
+ /// the group are very low but not high enough for an equalization to occur.
+ ///
+ [ViewVariables]
+ public int BreakdownCooldown = 0;
}