Use archived gas mixture in gas exchange comparison (#32088)

The comparison for doing gas exchange used current and not archived
moles. This could lead to update order-dependent gas spreading effects.

To fix this, convert TileAtmosphere's MolesArchived and
TemperatureArchived to a AirArchived, and use that in the comparison
method.

---------

Co-authored-by: PraxisMapper <praxismapper@gmail.com>
Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
drakewill-CRL
2024-09-30 01:14:07 -04:00
committed by GitHub
parent dd15a6862c
commit 7cf04dcb20
7 changed files with 47 additions and 27 deletions

View File

@@ -281,6 +281,17 @@ namespace Content.Server.Atmos.EntitySystems
return true; return true;
} }
/// <summary>
/// Compares two TileAtmospheres to see if they are within acceptable ranges for group processing to be enabled.
/// </summary>
public GasCompareResult CompareExchange(TileAtmosphere sample, TileAtmosphere otherSample)
{
if (sample.AirArchived == null || otherSample.AirArchived == null)
return GasCompareResult.NoExchange;
return CompareExchange(sample.AirArchived, otherSample.AirArchived);
}
/// <summary> /// <summary>
/// Compares two gas mixtures to see if they are within acceptable ranges for group processing to be enabled. /// Compares two gas mixtures to see if they are within acceptable ranges for group processing to be enabled.
/// </summary> /// </summary>

View File

@@ -270,7 +270,7 @@ public sealed partial class AtmosphereSystem
{ {
DebugTools.AssertNotNull(tile.Air); DebugTools.AssertNotNull(tile.Air);
DebugTools.Assert(tile.Air?.Immutable == false); DebugTools.Assert(tile.Air?.Immutable == false);
Array.Clear(tile.MolesArchived); tile.AirArchived = null;
tile.ArchivedCycle = 0; tile.ArchivedCycle = 0;
var count = 0; var count = 0;

View File

@@ -54,7 +54,7 @@ namespace Content.Server.Atmos.EntitySystems
} }
shouldShareAir = true; shouldShareAir = true;
} else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange) } else if (CompareExchange(tile, enemyTile) != GasCompareResult.NoExchange)
{ {
AddActiveTile(gridAtmosphere, enemyTile); AddActiveTile(gridAtmosphere, enemyTile);
if (ExcitedGroups) if (ExcitedGroups)
@@ -117,9 +117,8 @@ namespace Content.Server.Atmos.EntitySystems
private void Archive(TileAtmosphere tile, int fireCount) private void Archive(TileAtmosphere tile, int fireCount)
{ {
if (tile.Air != null) if (tile.Air != null)
tile.Air.Moles.AsSpan().CopyTo(tile.MolesArchived.AsSpan()); tile.AirArchived = new GasMixture(tile.Air);
tile.TemperatureArchived = tile.Temperature;
tile.ArchivedCycle = fireCount; tile.ArchivedCycle = fireCount;
} }
@@ -184,10 +183,10 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
public float GetHeatCapacityArchived(TileAtmosphere tile) public float GetHeatCapacityArchived(TileAtmosphere tile)
{ {
if (tile.Air == null) if (tile.AirArchived == null)
return tile.HeatCapacity; return tile.HeatCapacity;
return GetHeatCapacityCalculation(tile.MolesArchived!, tile.Space); return GetHeatCapacity(tile.AirArchived);
} }
/// <summary> /// <summary>
@@ -195,10 +194,11 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs) public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs)
{ {
if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer) if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer ||
tileReceiver.AirArchived == null || tileSharer.AirArchived == null)
return 0f; return 0f;
var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived; var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature;
var absTemperatureDelta = Math.Abs(temperatureDelta); var absTemperatureDelta = Math.Abs(temperatureDelta);
var oldHeatCapacity = 0f; var oldHeatCapacity = 0f;
var oldSharerHeatCapacity = 0f; var oldSharerHeatCapacity = 0f;
@@ -249,12 +249,12 @@ namespace Content.Server.Atmos.EntitySystems
// Transfer of thermal energy (via changed heat capacity) between self and sharer. // Transfer of thermal energy (via changed heat capacity) between self and sharer.
if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity) if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity)
{ {
receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.TemperatureArchived) + (heatCapacitySharerToThis * tileSharer.TemperatureArchived)) / newHeatCapacity; receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.AirArchived.Temperature) + (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature)) / newHeatCapacity;
} }
if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity) if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity)
{ {
sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.TemperatureArchived) + (heatCapacityToSharer * tileReceiver.TemperatureArchived)) / newSharerHeatCapacity; sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) + (heatCapacityToSharer * tileReceiver.AirArchived.Temperature)) / newSharerHeatCapacity;
} }
// Thermal energy of the system (self and sharer) is unchanged. // Thermal energy of the system (self and sharer) is unchanged.
@@ -273,7 +273,7 @@ namespace Content.Server.Atmos.EntitySystems
var moles = receiver.TotalMoles; var moles = receiver.TotalMoles;
var theirMoles = sharer.TotalMoles; var theirMoles = sharer.TotalMoles;
return (tileReceiver.TemperatureArchived * (moles + movedMoles)) - (tileSharer.TemperatureArchived * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; return (tileReceiver.AirArchived.Temperature * (moles + movedMoles)) - (tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume;
} }
/// <summary> /// <summary>
@@ -281,10 +281,11 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
public float TemperatureShare(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, float conductionCoefficient) public float TemperatureShare(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, float conductionCoefficient)
{ {
if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer) if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer ||
tileReceiver.AirArchived == null || tileSharer.AirArchived == null)
return 0f; return 0f;
var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived; var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature;
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider) if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
{ {
var heatCapacity = GetHeatCapacityArchived(tileReceiver); var heatCapacity = GetHeatCapacityArchived(tileReceiver);
@@ -310,10 +311,10 @@ namespace Content.Server.Atmos.EntitySystems
/// </summary> /// </summary>
public float TemperatureShare(TileAtmosphere tileReceiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity) public float TemperatureShare(TileAtmosphere tileReceiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity)
{ {
if (tileReceiver.Air is not {} receiver) if (tileReceiver.Air is not {} receiver || tileReceiver.AirArchived == null)
return 0; return 0;
var temperatureDelta = tileReceiver.TemperatureArchived - sharerTemperature; var temperatureDelta = tileReceiver.AirArchived.Temperature - sharerTemperature;
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider) if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
{ {
var heatCapacity = GetHeatCapacityArchived(tileReceiver); var heatCapacity = GetHeatCapacityArchived(tileReceiver);

View File

@@ -355,7 +355,7 @@ namespace Content.Server.Atmos.EntitySystems
continue; continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) if (otherTile2.Air != null && CompareExchange(otherTile2, tile) == GasCompareResult.NoExchange)
continue; continue;
AddActiveTile(gridAtmosphere, otherTile2); AddActiveTile(gridAtmosphere, otherTile2);

View File

@@ -231,7 +231,7 @@ namespace Content.Server.Atmos.EntitySystems
tile.MapAtmosphere = false; tile.MapAtmosphere = false;
atmos.MapTiles.Remove(tile); atmos.MapTiles.Remove(tile);
tile.Air = null; tile.Air = null;
Array.Clear(tile.MolesArchived); tile.AirArchived = null;
tile.ArchivedCycle = 0; tile.ArchivedCycle = 0;
tile.LastShare = 0f; tile.LastShare = 0f;
tile.Space = false; tile.Space = false;
@@ -261,7 +261,7 @@ namespace Content.Server.Atmos.EntitySystems
return; return;
tile.Air = null; tile.Air = null;
Array.Clear(tile.MolesArchived); tile.AirArchived = null;
tile.ArchivedCycle = 0; tile.ArchivedCycle = 0;
tile.LastShare = 0f; tile.LastShare = 0f;
tile.Hotspot = new Hotspot(); tile.Hotspot = new Hotspot();

View File

@@ -131,7 +131,10 @@ namespace Content.Server.Atmos.EntitySystems
private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient) private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)
{ {
var deltaTemperature = (tile.TemperatureArchived - other.TemperatureArchived); if (tile.AirArchived == null || other.AirArchived == null)
return;
var deltaTemperature = (tile.AirArchived.Temperature - other.AirArchived.Temperature);
if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider
&& tile.HeatCapacity != 0f && other.HeatCapacity != 0f) && tile.HeatCapacity != 0f && other.HeatCapacity != 0f)
{ {
@@ -145,11 +148,14 @@ namespace Content.Server.Atmos.EntitySystems
public void RadiateToSpace(TileAtmosphere tile) public void RadiateToSpace(TileAtmosphere tile)
{ {
if (tile.AirArchived == null)
return;
// Considering 0ºC as the break even point for radiation in and out. // Considering 0ºC as the break even point for radiation in and out.
if (tile.Temperature > Atmospherics.T0C) if (tile.Temperature > Atmospherics.T0C)
{ {
// Hardcoded space temperature. // Hardcoded space temperature.
var deltaTemperature = (tile.TemperatureArchived - Atmospherics.TCMB); var deltaTemperature = (tile.AirArchived.Temperature - Atmospherics.TCMB);
if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider)) if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
{ {
var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity * var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity *

View File

@@ -22,9 +22,6 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public float Temperature { get; set; } = Atmospherics.T20C; public float Temperature { get; set; } = Atmospherics.T20C;
[ViewVariables]
public float TemperatureArchived { get; set; } = Atmospherics.T20C;
[ViewVariables] [ViewVariables]
public TileAtmosphere? PressureSpecificTarget { get; set; } public TileAtmosphere? PressureSpecificTarget { get; set; }
@@ -93,12 +90,16 @@ namespace Content.Server.Atmos
[Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public GasMixture? Air { get; set; } public GasMixture? Air { get; set; }
/// <summary>
/// Like Air, but a copy stored each atmos tick before tile processing takes place. This lets us update Air
/// in-place without affecting the results based on update order.
/// </summary>
[ViewVariables]
public GasMixture? AirArchived;
[DataField("lastShare")] [DataField("lastShare")]
public float LastShare; public float LastShare;
[ViewVariables]
public readonly float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
GasMixture IGasMixtureHolder.Air GasMixture IGasMixtureHolder.Air
{ {
get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature }; get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature };
@@ -139,6 +140,7 @@ namespace Content.Server.Atmos
GridIndex = gridIndex; GridIndex = gridIndex;
GridIndices = gridIndices; GridIndices = gridIndices;
Air = mixture; Air = mixture;
AirArchived = Air != null ? Air.Clone() : null;
Space = space; Space = space;
if(immutable) if(immutable)
@@ -153,7 +155,7 @@ namespace Content.Server.Atmos
NoGridTile = other.NoGridTile; NoGridTile = other.NoGridTile;
MapAtmosphere = other.MapAtmosphere; MapAtmosphere = other.MapAtmosphere;
Air = other.Air?.Clone(); Air = other.Air?.Clone();
Array.Copy(other.MolesArchived, MolesArchived, MolesArchived.Length); AirArchived = Air != null ? Air.Clone() : null;
} }
public TileAtmosphere() public TileAtmosphere()