mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-02-14 19:29:53 +01:00
Atmos GasSpecificHeats in shared (#42136)
This commit is contained in:
83
Content.Benchmarks/HeatCapacityBenchmark.cs
Normal file
83
Content.Benchmarks/HeatCapacityBenchmark.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Content.IntegrationTests;
|
||||||
|
using Content.IntegrationTests.Pair;
|
||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Robust.Shared;
|
||||||
|
using Robust.Shared.Analyzers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Benchmarks;
|
||||||
|
|
||||||
|
[Virtual]
|
||||||
|
[GcServer(true)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class HeatCapacityBenchmark
|
||||||
|
{
|
||||||
|
private TestPair _pair = default!;
|
||||||
|
private IEntityManager _sEntMan = default!;
|
||||||
|
private IEntityManager _cEntMan = default!;
|
||||||
|
private Client.Atmos.EntitySystems.AtmosphereSystem _cAtmos = default!;
|
||||||
|
private AtmosphereSystem _sAtmos = default!;
|
||||||
|
private GasMixture _mix;
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public async Task SetupAsync()
|
||||||
|
{
|
||||||
|
ProgramShared.PathOffset = "../../../../";
|
||||||
|
PoolManager.Startup();
|
||||||
|
_pair = await PoolManager.GetServerClient();
|
||||||
|
await _pair.Connect();
|
||||||
|
_cEntMan = _pair.Client.ResolveDependency<IEntityManager>();
|
||||||
|
_sEntMan = _pair.Server.ResolveDependency<IEntityManager>();
|
||||||
|
_cAtmos = _cEntMan.System<Client.Atmos.EntitySystems.AtmosphereSystem>();
|
||||||
|
_sAtmos = _sEntMan.System<AtmosphereSystem>();
|
||||||
|
|
||||||
|
const float volume = 2500f;
|
||||||
|
const float temperature = 293.15f;
|
||||||
|
|
||||||
|
const float o2 = 12.3f;
|
||||||
|
const float n2 = 45.6f;
|
||||||
|
const float co2 = 0.42f;
|
||||||
|
const float plasma = 0.05f;
|
||||||
|
|
||||||
|
_mix = new GasMixture(volume) { Temperature = temperature };
|
||||||
|
|
||||||
|
_mix.AdjustMoles(Gas.Oxygen, o2);
|
||||||
|
_mix.AdjustMoles(Gas.Nitrogen, n2);
|
||||||
|
_mix.AdjustMoles(Gas.CarbonDioxide, co2);
|
||||||
|
_mix.AdjustMoles(Gas.Plasma, plasma);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task ClientHeatCapacityBenchmark()
|
||||||
|
{
|
||||||
|
await _pair.Client.WaitPost(delegate
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 10000; i++)
|
||||||
|
{
|
||||||
|
_cAtmos.GetHeatCapacity(_mix, applyScaling: true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public async Task ServerHeatCapacityBenchmark()
|
||||||
|
{
|
||||||
|
await _pair.Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
for (var i = 0; i < 10000; i++)
|
||||||
|
{
|
||||||
|
_sAtmos.GetHeatCapacity(_mix, applyScaling: true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[GlobalCleanup]
|
||||||
|
public async Task CleanupAsync()
|
||||||
|
{
|
||||||
|
await _pair.DisposeAsync();
|
||||||
|
PoolManager.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
Normal file
35
Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.EntitySystems;
|
||||||
|
|
||||||
|
public sealed partial class AtmosphereSystem
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Partial class for operations involving GasMixtures.
|
||||||
|
|
||||||
|
Any method that is overridden here is usually because the server-sided implementation contains
|
||||||
|
code that would escape sandbox. As such these methods are overridden here with a safe
|
||||||
|
implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected override float GetHeatCapacityCalculation(float[] moles, bool space)
|
||||||
|
{
|
||||||
|
// Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms.
|
||||||
|
if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
||||||
|
{
|
||||||
|
return Atmospherics.SpaceHeatCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicit stackalloc call is banned on client tragically.
|
||||||
|
// the JIT does not stackalloc this during runtime,
|
||||||
|
// though this isnt the hottest code path so it should be fine
|
||||||
|
// the gc can eat a little as a treat
|
||||||
|
var tmp = new float[moles.Length];
|
||||||
|
NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp);
|
||||||
|
// Adjust heat capacity by speedup, because this is primarily what
|
||||||
|
// determines how quickly gases heat up/cool.
|
||||||
|
return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using Robust.Shared.GameStates;
|
|||||||
|
|
||||||
namespace Content.Client.Atmos.EntitySystems;
|
namespace Content.Client.Atmos.EntitySystems;
|
||||||
|
|
||||||
public sealed class AtmosphereSystem : SharedAtmosphereSystem
|
public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||||
{
|
{
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace Content.IntegrationTests.Tests.Atmos;
|
|||||||
public abstract class AtmosTest : InteractionTest
|
public abstract class AtmosTest : InteractionTest
|
||||||
{
|
{
|
||||||
protected AtmosphereSystem SAtmos = default!;
|
protected AtmosphereSystem SAtmos = default!;
|
||||||
|
protected Content.Client.Atmos.EntitySystems.AtmosphereSystem CAtmos = default!;
|
||||||
protected EntityLookupSystem LookupSystem = default!;
|
protected EntityLookupSystem LookupSystem = default!;
|
||||||
|
|
||||||
protected Entity<GridAtmosphereComponent> RelevantAtmos;
|
protected Entity<GridAtmosphereComponent> RelevantAtmos;
|
||||||
@@ -38,6 +39,7 @@ public abstract class AtmosTest : InteractionTest
|
|||||||
await base.Setup();
|
await base.Setup();
|
||||||
|
|
||||||
SAtmos = SEntMan.System<AtmosphereSystem>();
|
SAtmos = SEntMan.System<AtmosphereSystem>();
|
||||||
|
CAtmos = CEntMan.System<Content.Client.Atmos.EntitySystems.AtmosphereSystem>();
|
||||||
LookupSystem = SEntMan.System<EntityLookupSystem>();
|
LookupSystem = SEntMan.System<EntityLookupSystem>();
|
||||||
|
|
||||||
SEntMan.TryGetComponent<GridAtmosphereComponent>(MapData.Grid, out var gridAtmosComp);
|
SEntMan.TryGetComponent<GridAtmosphereComponent>(MapData.Grid, out var gridAtmosComp);
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
using Content.Client.Atmos.EntitySystems;
|
||||||
|
using Content.IntegrationTests.Pair;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.UnitTesting;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.Atmos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for asserting that various gas specific heat operations agree with each other and do not deviate
|
||||||
|
/// across client and server.
|
||||||
|
/// </summary>
|
||||||
|
[TestOf(nameof(SharedAtmosphereSystem))]
|
||||||
|
public sealed class SharedGasSpecificHeatsTest
|
||||||
|
{
|
||||||
|
private IConfigurationManager _sConfig;
|
||||||
|
private IConfigurationManager _cConfig;
|
||||||
|
|
||||||
|
private TestPair _pair = default!;
|
||||||
|
|
||||||
|
private RobustIntegrationTest.ServerIntegrationInstance Server => _pair.Server;
|
||||||
|
private RobustIntegrationTest.ClientIntegrationInstance Client => _pair.Client;
|
||||||
|
|
||||||
|
private IEntityManager _sEntMan = default!;
|
||||||
|
private Content.Server.Atmos.EntitySystems.AtmosphereSystem _sAtmos = default!;
|
||||||
|
|
||||||
|
private IEntityManager _cEntMan = default!;
|
||||||
|
private AtmosphereSystem _cAtmos = default!;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public async Task SetUp()
|
||||||
|
{
|
||||||
|
var poolSettings = new PoolSettings
|
||||||
|
{
|
||||||
|
Connected = true,
|
||||||
|
};
|
||||||
|
_pair = await PoolManager.GetServerClient(poolSettings);
|
||||||
|
|
||||||
|
_sEntMan = Server.ResolveDependency<IEntityManager>();
|
||||||
|
_cEntMan = Client.ResolveDependency<IEntityManager>();
|
||||||
|
|
||||||
|
_sAtmos = _sEntMan.System<Content.Server.Atmos.EntitySystems.AtmosphereSystem>();
|
||||||
|
_cAtmos = _cEntMan.System<AtmosphereSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asserts that the cached gas specific heat arrays agree with each other.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task GasSpecificHeats_Agree()
|
||||||
|
{
|
||||||
|
var serverSpecificHeats = Array.Empty<float>();
|
||||||
|
var clientSpecificHeats = Array.Empty<float>();
|
||||||
|
await Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
serverSpecificHeats = _sAtmos.GasSpecificHeats;
|
||||||
|
});
|
||||||
|
|
||||||
|
await Client.WaitPost(delegate
|
||||||
|
{
|
||||||
|
clientSpecificHeats = _cAtmos.GasSpecificHeats;
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(serverSpecificHeats,
|
||||||
|
Is.EqualTo(clientSpecificHeats),
|
||||||
|
"Server and client gas specific heat arrays do not agree.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asserts that heat capacity calculations agree for the same gas mixture.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task HeatCapacity_Agree()
|
||||||
|
{
|
||||||
|
const float volume = 2500f;
|
||||||
|
const float temperature = 293.15f;
|
||||||
|
|
||||||
|
const float o2 = 12.3f;
|
||||||
|
const float n2 = 45.6f;
|
||||||
|
const float co2 = 0.42f;
|
||||||
|
const float plasma = 0.05f;
|
||||||
|
|
||||||
|
var serverScaled = 0f;
|
||||||
|
var serverUnscaled = 0f;
|
||||||
|
var clientScaled = 0f;
|
||||||
|
var clientUnscaled = 0f;
|
||||||
|
|
||||||
|
await Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
var mix = new GasMixture(volume) { Temperature = temperature };
|
||||||
|
mix.AdjustMoles(Gas.Oxygen, o2);
|
||||||
|
mix.AdjustMoles(Gas.Nitrogen, n2);
|
||||||
|
mix.AdjustMoles(Gas.CarbonDioxide, co2);
|
||||||
|
mix.AdjustMoles(Gas.Plasma, plasma);
|
||||||
|
|
||||||
|
serverScaled = _sAtmos.GetHeatCapacity(mix, applyScaling: true);
|
||||||
|
serverUnscaled = _sAtmos.GetHeatCapacity(mix, applyScaling: false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Client.WaitPost(delegate
|
||||||
|
{
|
||||||
|
var mix = new GasMixture(volume) { Temperature = temperature };
|
||||||
|
mix.AdjustMoles(Gas.Oxygen, o2);
|
||||||
|
mix.AdjustMoles(Gas.Nitrogen, n2);
|
||||||
|
mix.AdjustMoles(Gas.CarbonDioxide, co2);
|
||||||
|
mix.AdjustMoles(Gas.Plasma, plasma);
|
||||||
|
|
||||||
|
clientScaled = _cAtmos.GetHeatCapacity(mix, applyScaling: true);
|
||||||
|
clientUnscaled = _cAtmos.GetHeatCapacity(mix, applyScaling: false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// none of these should be exploding or nonzero.
|
||||||
|
// they could potentially agree at insane values and pass the test
|
||||||
|
// so check for if they're sane.
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(serverScaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on server with scaling is not greater than zero.");
|
||||||
|
Assert.That(serverUnscaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on server without scaling is not greater than zero.");
|
||||||
|
Assert.That(clientScaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on client with scaling is not greater than zero.");
|
||||||
|
Assert.That(clientUnscaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on client without scaling is not greater than zero.");
|
||||||
|
|
||||||
|
Assert.That(float.IsFinite(serverScaled),
|
||||||
|
Is.True,
|
||||||
|
"Heat capacity calculated on server with scaling is not finite.");
|
||||||
|
Assert.That(float.IsFinite(serverUnscaled),
|
||||||
|
Is.True,
|
||||||
|
"Heat capacity calculated on server without scaling is not finite.");
|
||||||
|
Assert.That(float.IsFinite(clientScaled),
|
||||||
|
Is.True,
|
||||||
|
"Heat capacity calculated on client with scaling is not finite.");
|
||||||
|
Assert.That(float.IsFinite(clientUnscaled),
|
||||||
|
Is.True,
|
||||||
|
"Heat capacity calculated on client without scaling is not finite.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const float epsilon = 1e-4f;
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(serverScaled,
|
||||||
|
Is.EqualTo(clientScaled).Within(epsilon),
|
||||||
|
"Heat capacity calculated with scaling does not agree between client and server.");
|
||||||
|
Assert.That(serverUnscaled,
|
||||||
|
Is.EqualTo(clientUnscaled).Within(epsilon),
|
||||||
|
"Heat capacity calculated without scaling does not agree between client and server.");
|
||||||
|
|
||||||
|
Assert.That(serverUnscaled,
|
||||||
|
Is.EqualTo(serverScaled * _sAtmos.HeatScale).Within(epsilon),
|
||||||
|
"Heat capacity calculated on server without scaling does not equal scaled value multiplied by HeatScale.");
|
||||||
|
Assert.That(clientUnscaled,
|
||||||
|
Is.EqualTo(clientScaled * _cAtmos.HeatScale).Within(epsilon),
|
||||||
|
"Heat capacity calculated on client without scaling does not equal scaled value multiplied by HeatScale.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HeatScale CVAR is required for specific heat calculations.
|
||||||
|
/// Assert that they agree across client and server, and that changing the CVAR
|
||||||
|
/// replicates properly and updates the cached value.
|
||||||
|
/// Also assert that calculations using the updated HeatScale agree properly.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task HeatScaleCVar_Replicates_Agree()
|
||||||
|
{
|
||||||
|
// ensure that replicated value changes by testing a new value
|
||||||
|
const float newHeatScale = 13f;
|
||||||
|
|
||||||
|
_sConfig = Server.ResolveDependency<IConfigurationManager>();
|
||||||
|
_cConfig = Client.ResolveDependency<IConfigurationManager>();
|
||||||
|
|
||||||
|
await Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
_sConfig.SetCVar(CCVars.AtmosHeatScale, newHeatScale);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Server.WaitRunTicks(5);
|
||||||
|
await Client.WaitRunTicks(5);
|
||||||
|
|
||||||
|
// assert agreement between client and server
|
||||||
|
float serverCVar = 0;
|
||||||
|
float clientCVar = 0;
|
||||||
|
float serverHeatScale = 0;
|
||||||
|
float clientHeatScale = 0;
|
||||||
|
|
||||||
|
await Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
serverCVar = _sConfig.GetCVar(CCVars.AtmosHeatScale);
|
||||||
|
serverHeatScale = _sAtmos.HeatScale;
|
||||||
|
});
|
||||||
|
|
||||||
|
await Client.WaitPost(delegate
|
||||||
|
{
|
||||||
|
clientCVar = _cConfig.GetCVar(CCVars.AtmosHeatScale);
|
||||||
|
clientHeatScale = _cAtmos.HeatScale;
|
||||||
|
});
|
||||||
|
|
||||||
|
const float epsilon = 1e-4f;
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(serverCVar,
|
||||||
|
Is.EqualTo(newHeatScale).Within(epsilon),
|
||||||
|
"Server CVAR value for AtmosHeatScale does not equal the set value.");
|
||||||
|
Assert.That(clientCVar,
|
||||||
|
Is.EqualTo(newHeatScale).Within(epsilon),
|
||||||
|
"Client CVAR value for AtmosHeatScale does not equal the set value.");
|
||||||
|
|
||||||
|
Assert.That(serverHeatScale,
|
||||||
|
Is.EqualTo(newHeatScale).Within(epsilon),
|
||||||
|
"Server cached HeatScale does not equal the set CVAR value.");
|
||||||
|
Assert.That(clientHeatScale,
|
||||||
|
Is.EqualTo(newHeatScale).Within(epsilon),
|
||||||
|
"Client cached HeatScale does not equal the set CVAR value.");
|
||||||
|
|
||||||
|
Assert.That(serverHeatScale,
|
||||||
|
Is.EqualTo(clientHeatScale).Within(epsilon),
|
||||||
|
"Client and server cached HeatScale values do not agree.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that anything calculated using the shared HeatScale agrees properly
|
||||||
|
const float volume = 2500f;
|
||||||
|
const float temperature = 293.15f;
|
||||||
|
|
||||||
|
var sScaled = 0f;
|
||||||
|
var sUnscaled = 0f;
|
||||||
|
var cScaled = 0f;
|
||||||
|
var cUnscaled = 0f;
|
||||||
|
|
||||||
|
await Server.WaitPost(delegate
|
||||||
|
{
|
||||||
|
var mix = new GasMixture(volume) { Temperature = temperature };
|
||||||
|
mix.AdjustMoles(Gas.Oxygen, 10f);
|
||||||
|
mix.AdjustMoles(Gas.Nitrogen, 20f);
|
||||||
|
|
||||||
|
sScaled = _sAtmos.GetHeatCapacity(mix, applyScaling: true);
|
||||||
|
sUnscaled = _sAtmos.GetHeatCapacity(mix, applyScaling: false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Client.WaitPost(delegate
|
||||||
|
{
|
||||||
|
var mix = new GasMixture(volume) { Temperature = temperature };
|
||||||
|
mix.AdjustMoles(Gas.Oxygen, 10f);
|
||||||
|
mix.AdjustMoles(Gas.Nitrogen, 20f);
|
||||||
|
|
||||||
|
cScaled = _cAtmos.GetHeatCapacity(mix, applyScaling: true);
|
||||||
|
cUnscaled = _cAtmos.GetHeatCapacity(mix, applyScaling: false);
|
||||||
|
});
|
||||||
|
|
||||||
|
using (Assert.EnterMultipleScope())
|
||||||
|
{
|
||||||
|
Assert.That(sScaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on server with scaling is not greater than zero after CVAR change.");
|
||||||
|
Assert.That(cScaled,
|
||||||
|
Is.GreaterThan(0f),
|
||||||
|
"Heat capacity calculated on client with scaling is not greater than zero after CVAR change.");
|
||||||
|
|
||||||
|
Assert.That(sUnscaled,
|
||||||
|
Is.EqualTo(sScaled * serverHeatScale).Within(epsilon),
|
||||||
|
"Heat capacity calculated on server without scaling does not equal scaled value multiplied by updated HeatScale.");
|
||||||
|
Assert.That(cUnscaled,
|
||||||
|
Is.EqualTo(cScaled * clientHeatScale).Within(epsilon),
|
||||||
|
"Heat capacity calculated on client without scaling does not equal scaled value multiplied by updated HeatScale.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
public float AtmosMaxProcessTime { get; private set; }
|
public float AtmosMaxProcessTime { get; private set; }
|
||||||
public float AtmosTickRate { get; private set; }
|
public float AtmosTickRate { get; private set; }
|
||||||
public float Speedup { get; private set; }
|
public float Speedup { get; private set; }
|
||||||
public float HeatScale { get; private set; }
|
|
||||||
public bool DeltaPressureDamage { get; private set; }
|
public bool DeltaPressureDamage { get; private set; }
|
||||||
public int DeltaPressureParallelProcessPerIteration { get; private set; }
|
public int DeltaPressureParallelProcessPerIteration { get; private set; }
|
||||||
public int DeltaPressureParallelBatchSize { get; private set; }
|
public int DeltaPressureParallelBatchSize { get; private set; }
|
||||||
@@ -55,7 +54,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
Subs.CVar(_cfg, CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
Subs.CVar(_cfg, CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
||||||
Subs.CVar(_cfg, CCVars.AtmosTickRate, value => AtmosTickRate = value, true);
|
Subs.CVar(_cfg, CCVars.AtmosTickRate, value => AtmosTickRate = value, true);
|
||||||
Subs.CVar(_cfg, CCVars.AtmosSpeedup, value => Speedup = value, true);
|
Subs.CVar(_cfg, CCVars.AtmosSpeedup, value => Speedup = value, true);
|
||||||
Subs.CVar(_cfg, CCVars.AtmosHeatScale, value => { HeatScale = value; InitializeGases(); }, true);
|
|
||||||
Subs.CVar(_cfg, CCVars.ExcitedGroups, value => ExcitedGroups = value, true);
|
Subs.CVar(_cfg, CCVars.ExcitedGroups, value => ExcitedGroups = value, true);
|
||||||
Subs.CVar(_cfg, CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true);
|
Subs.CVar(_cfg, CCVars.ExcitedGroupsSpaceIsAllConsuming, value => ExcitedGroupsSpaceIsAllConsuming = value, true);
|
||||||
Subs.CVar(_cfg, CCVars.DeltaPressureDamage, value => DeltaPressureDamage = value, true);
|
Subs.CVar(_cfg, CCVars.DeltaPressureDamage, value => DeltaPressureDamage = value, true);
|
||||||
|
|||||||
@@ -13,53 +13,23 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||||
|
|
||||||
private GasReactionPrototype[] _gasReactions = Array.Empty<GasReactionPrototype>();
|
private GasReactionPrototype[] _gasReactions = [];
|
||||||
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of gas reactions ordered by priority.
|
/// List of gas reactions ordered by priority.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<GasReactionPrototype> GasReactions => _gasReactions;
|
public IEnumerable<GasReactionPrototype> GasReactions => _gasReactions;
|
||||||
|
|
||||||
/// <summary>
|
public override void InitializeGases()
|
||||||
/// Cached array of gas specific heats.
|
|
||||||
/// </summary>
|
|
||||||
public float[] GasSpecificHeats => _gasSpecificHeats;
|
|
||||||
|
|
||||||
private void InitializeGases()
|
|
||||||
{
|
{
|
||||||
|
base.InitializeGases();
|
||||||
|
|
||||||
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray();
|
_gasReactions = _protoMan.EnumeratePrototypes<GasReactionPrototype>().ToArray();
|
||||||
Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority));
|
Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority));
|
||||||
|
|
||||||
Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4));
|
|
||||||
|
|
||||||
for (var i = 0; i < GasPrototypes.Length; i++)
|
|
||||||
{
|
|
||||||
_gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the heat capacity for a gas mixture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mixture">The mixture whose heat capacity should be calculated</param>
|
|
||||||
/// <param name="applyScaling"> Whether the internal heat capacity scaling should be applied. This should not be
|
|
||||||
/// used outside of atmospheric related heat transfer.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public float GetHeatCapacity(GasMixture mixture, bool applyScaling)
|
|
||||||
{
|
|
||||||
var scale = GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable);
|
|
||||||
|
|
||||||
// By default GetHeatCapacityCalculation() has the heat-scale divisor pre-applied.
|
|
||||||
// So if we want the un-scaled heat capacity, we have to multiply by the scale.
|
|
||||||
return applyScaling ? scale : scale * HeatScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetHeatCapacity(GasMixture mixture)
|
|
||||||
=> GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private float GetHeatCapacityCalculation(float[] moles, bool space)
|
protected override float GetHeatCapacityCalculation(float[] moles, bool space)
|
||||||
{
|
{
|
||||||
// Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms.
|
// Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms.
|
||||||
if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.EntitySystems;
|
||||||
|
|
||||||
|
public abstract partial class SharedAtmosphereSystem
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Partial class for storing shared configuration values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public float HeatScale { get; private set; }
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "BadExpressionBracesLineBreaks")]
|
||||||
|
[SuppressMessage("ReSharper", "MultipleStatementsOnOneLine")]
|
||||||
|
private void InitializeCVars()
|
||||||
|
{
|
||||||
|
Subs.CVar(_cfg, CCVars.AtmosHeatScale, value => { HeatScale = value; InitializeGases(); }, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Content.Shared.Atmos.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.EntitySystems;
|
||||||
|
|
||||||
|
public abstract partial class SharedAtmosphereSystem
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Partial class for operations involving GasMixtures.
|
||||||
|
|
||||||
|
Sometimes methods here are abstract because they need different client/server implementations
|
||||||
|
due to sandboxing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached array of gas specific heats.
|
||||||
|
/// </summary>
|
||||||
|
public float[] GasSpecificHeats => _gasSpecificHeats;
|
||||||
|
private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases];
|
||||||
|
|
||||||
|
public string?[] GasReagents = new string[Atmospherics.TotalNumberOfGases];
|
||||||
|
protected readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases];
|
||||||
|
|
||||||
|
public virtual void InitializeGases()
|
||||||
|
{
|
||||||
|
foreach (var gas in Enum.GetValues<Gas>())
|
||||||
|
{
|
||||||
|
var idx = (int)gas;
|
||||||
|
// Log an error if the corresponding prototype isn't found
|
||||||
|
if (!_prototypeManager.TryIndex<GasPrototype>(gas.ToString(), out var gasPrototype))
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to find corresponding {nameof(GasPrototype)} for gas ID {(int)gas} ({gas}) with expected ID \"{gas.ToString()}\". Is your prototype named correctly?");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GasPrototypes[idx] = gasPrototype;
|
||||||
|
GasReagents[idx] = gasPrototype.Reagent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4));
|
||||||
|
|
||||||
|
for (var i = 0; i < GasPrototypes.Length; i++)
|
||||||
|
{
|
||||||
|
_gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the heat capacity for a gas mixture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mixture">The mixture whose heat capacity should be calculated</param>
|
||||||
|
/// <param name="applyScaling"> Whether the internal heat capacity scaling should be applied. This should not be
|
||||||
|
/// used outside of atmospheric related heat transfer.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public float GetHeatCapacity(GasMixture mixture, bool applyScaling)
|
||||||
|
{
|
||||||
|
var scale = GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable);
|
||||||
|
|
||||||
|
// By default GetHeatCapacityCalculation() has the heat-scale divisor pre-applied.
|
||||||
|
// So if we want the un-scaled heat capacity, we have to multiply by the scale.
|
||||||
|
return applyScaling ? scale : scale * HeatScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float GetHeatCapacity(GasMixture mixture)
|
||||||
|
{
|
||||||
|
return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the heat capacity for a <see cref="GasMixture"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="moles">The moles array of the <see cref="GasMixture"/></param>
|
||||||
|
/// <param name="space">Whether this <see cref="GasMixture"/> represents space,
|
||||||
|
/// and thus experiences space-specific mechanics (we cheat and make it a bit cooler).</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected abstract float GetHeatCapacityCalculation(float[] moles, bool space);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Shared.Atmos.Prototypes;
|
using Content.Shared.Atmos.Prototypes;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Systems;
|
using Content.Shared.Body.Systems;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Atmos.EntitySystems
|
namespace Content.Shared.Atmos.EntitySystems
|
||||||
@@ -9,13 +10,10 @@ namespace Content.Shared.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly SharedInternalsSystem _internals = default!;
|
[Dependency] private readonly SharedInternalsSystem _internals = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
|
||||||
private EntityQuery<InternalsComponent> _internalsQuery;
|
private EntityQuery<InternalsComponent> _internalsQuery;
|
||||||
|
|
||||||
public string?[] GasReagents = new string[Atmospherics.TotalNumberOfGases];
|
|
||||||
|
|
||||||
protected readonly GasPrototype[] GasPrototypes = new GasPrototype[Atmospherics.TotalNumberOfGases];
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -23,19 +21,8 @@ namespace Content.Shared.Atmos.EntitySystems
|
|||||||
_internalsQuery = GetEntityQuery<InternalsComponent>();
|
_internalsQuery = GetEntityQuery<InternalsComponent>();
|
||||||
|
|
||||||
InitializeBreathTool();
|
InitializeBreathTool();
|
||||||
|
InitializeGases();
|
||||||
foreach (var gas in Enum.GetValues<Gas>())
|
InitializeCVars();
|
||||||
{
|
|
||||||
var idx = (int)gas;
|
|
||||||
// Log an error if the corresponding prototype isn't found
|
|
||||||
if (!_prototypeManager.TryIndex<GasPrototype>(gas.ToString(), out var gasPrototype))
|
|
||||||
{
|
|
||||||
Log.Error($"Failed to find corresponding {nameof(GasPrototype)} for gas ID {(int)gas} ({gas}) with expected ID \"{gas.ToString()}\". Is your prototype named correctly?");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
GasPrototypes[idx] = gasPrototype;
|
|
||||||
GasReagents[idx] = gasPrototype.Reagent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GasPrototype GetGas(int gasId) => GasPrototypes[gasId];
|
public GasPrototype GetGas(int gasId) => GasPrototypes[gasId];
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ public sealed partial class CCVars
|
|||||||
/// gases heat up and cool down 64x faster than real life.
|
/// gases heat up and cool down 64x faster than real life.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly CVarDef<float> AtmosHeatScale =
|
public static readonly CVarDef<float> AtmosHeatScale =
|
||||||
CVarDef.Create("atmos.heat_scale", 8f, CVar.SERVERONLY);
|
CVarDef.Create("atmos.heat_scale", 8f, CVar.REPLICATED | CVar.SERVER);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum explosion radius for explosions caused by bursting a gas tank ("max caps").
|
/// Maximum explosion radius for explosions caused by bursting a gas tank ("max caps").
|
||||||
|
|||||||
Reference in New Issue
Block a user