--- /dev/null
+using System.Linq;
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.Components;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Prototypes;
+
+namespace Content.IntegrationTests.Tests.Atmos;
+
+[TestFixture]
+[TestOf(typeof(Atmospherics))]
+public sealed class GasArrayTest
+{
+ private const string GasTankTestDummyId = "GasTankTestDummy";
+
+ private const string GasTankLegacyTestDummyId = "GasTankLegacyTestDummy";
+
+ [TestPrototypes]
+ private const string Prototypes = $@"
+- type: entity
+ id: {GasTankTestDummyId}
+ components:
+ - type: GasTank
+ air:
+ volume: 5
+ moles:
+ Frezon: 20
+ Oxygen: 10
+
+- type: entity
+ id: {GasTankLegacyTestDummyId}
+ components:
+ - type: GasTank
+ air:
+ volume: 5
+ moles:
+ - 0
+ - 0
+ - 0
+ - 10
+";
+
+ [Test]
+ public async Task TestGasArrayDeserialization()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var server = pair.Server;
+
+ var compFactory = server.ResolveDependency<IComponentFactory>();
+ var prototypeManager = server.ResolveDependency<IPrototypeManager>();
+
+ await server.WaitAssertion(() =>
+ {
+ var gasTank = prototypeManager.Index(GasTankTestDummyId);
+ Assert.Multiple(() =>
+ {
+ Assert.That(gasTank.TryGetComponent<GasTankComponent>(out var gasTankComponent, compFactory));
+
+ Assert.That(gasTankComponent!.Air.GetMoles(Gas.Oxygen), Is.EqualTo(10));
+ Assert.That(gasTankComponent!.Air.GetMoles(Gas.Frezon), Is.EqualTo(20));
+ foreach (var gas in Enum.GetValues<Gas>().Where(p => p != Gas.Oxygen && p != Gas.Frezon))
+ {
+ Assert.That(gasTankComponent!.Air.GetMoles(gas), Is.EqualTo(0));
+ }
+ });
+
+ var legacyGasTank = prototypeManager.Index(GasTankLegacyTestDummyId);
+ Assert.Multiple(() =>
+ {
+ Assert.That(legacyGasTank.TryGetComponent<GasTankComponent>(out var gasTankComponent, compFactory));
+
+ Assert.That(gasTankComponent!.Air.GetMoles(3), Is.EqualTo(10));
+
+ // Iterate through all other gases: check for 0 values
+ for (var i = 0; i < Atmospherics.AdjustedNumberOfGases; i++)
+ {
+ if (i == 3) // our case with a value.
+ continue;
+
+ Assert.That(gasTankComponent!.Air.GetMoles(i), Is.EqualTo(0));
+ }
+ });
+ });
+ await pair.CleanReturnAsync();
+ }
+}
continue;
var doReaction = true;
- for (var i = 0; i < prototype.MinimumRequirements.Length; i++)
+ for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{
- if(i >= Atmospherics.TotalNumberOfGases)
- throw new IndexOutOfRangeException("Reaction Gas Minimum Requirements Array Prototype exceeds total number of gases!");
-
var req = prototype.MinimumRequirements[i];
if (!(mixture.GetMoles(i) < req))
/// <summary>
/// Minimum gas amount requirements.
/// </summary>
- [DataField("minimumRequirements")]
+ [DataField("minimumRequirements", customTypeSerializer: typeof(GasArraySerializer))]
public float[] MinimumRequirements { get; private set; } = new float[Atmospherics.TotalNumberOfGases];
/// <summary>
--- /dev/null
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.Manager;
+using Robust.Shared.Serialization.Markdown;
+using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Sequence;
+using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+
+namespace Content.Shared.Atmos;
+
+public sealed class GasArraySerializer : ITypeSerializer<float[], SequenceDataNode>, ITypeSerializer<float[], MappingDataNode>
+{
+ public ValidationNode Validate(ISerializationManager serializationManager,
+ SequenceDataNode node,
+ IDependencyCollection dependencies,
+ ISerializationContext? context = null)
+ {
+ var list = new List<ValidationNode>();
+
+ foreach (var elem in node.Sequence)
+ {
+ list.Add(serializationManager.ValidateNode<float>(elem, context));
+ }
+
+ return new ValidatedSequenceNode(list);
+ }
+
+ public float[] Read(ISerializationManager serializationManager,
+ SequenceDataNode node,
+ IDependencyCollection dependencies,
+ SerializationHookContext hookCtx,
+ ISerializationContext? context = null,
+ ISerializationManager.InstantiationDelegate<float[]>? instanceProvider = null)
+ {
+ var list = instanceProvider != null ? instanceProvider() : new float[Atmospherics.AdjustedNumberOfGases];
+
+ for (var i = 0; i < node.Sequence.Count; i++)
+ {
+ list[i] = serializationManager.Read<float>(node.Sequence[i], hookCtx, context);
+ }
+
+ return list;
+ }
+
+ public ValidationNode Validate(ISerializationManager serializationManager,
+ MappingDataNode node,
+ IDependencyCollection dependencies,
+ ISerializationContext? context = null)
+ {
+ var dict = new Dictionary<ValidationNode, ValidationNode>();
+
+ foreach (var (key, value) in node.Children)
+ {
+ ValidationNode keyNode = Enum.TryParse<Gas>(key, out _)
+ ? new ValidatedValueNode(node.GetKeyNode(key))
+ : new ErrorNode(node.GetKeyNode(key), $"Failed to parse Gas: {key}");
+
+ dict.Add(keyNode, serializationManager.ValidateNode<float>(value, context));
+ }
+
+ return new ValidatedMappingNode(dict);
+ }
+
+ public float[] Read(ISerializationManager serializationManager,
+ MappingDataNode node,
+ IDependencyCollection dependencies,
+ SerializationHookContext hookCtx,
+ ISerializationContext? context = null,
+ ISerializationManager.InstantiationDelegate<float[]>? instanceProvider = null)
+ {
+ var list = instanceProvider != null ? instanceProvider() : new float[Atmospherics.AdjustedNumberOfGases];
+
+ foreach (var (gas, value) in node.Children)
+ {
+ // In the event that an invalid gas got serialized into something,
+ // we simply ignore it and continue reading.
+ // Errors should already be caught by Validate().
+ if (!Enum.TryParse<Gas>(gas, out var gasEnum))
+ continue;
+
+ list[(int)gasEnum] = serializationManager.Read<float>(value, hookCtx, context);
+ }
+
+ return list;
+ }
+
+ public DataNode Write(ISerializationManager serializationManager,
+ float[] value,
+ IDependencyCollection dependencies,
+ bool alwaysWrite = false,
+ ISerializationContext? context = null)
+ {
+ var mapping = new MappingDataNode();
+
+ for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
+ {
+ if (value[i] <= 0)
+ continue;
+
+ mapping.Add(((Gas) i).ToString(), serializationManager.WriteValue(value[i], alwaysWrite, context));
+ }
+
+ return mapping;
+ }
+}
// No access, to ensure immutable mixtures are never accidentally mutated.
[Access(typeof(SharedAtmosphereSystem), typeof(SharedAtmosDebugOverlaySystem), typeof(GasEnumerator), Other = AccessPermissions.None)]
- [DataField]
+ [DataField(customTypeSerializer: typeof(GasArraySerializer))]
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
public float this[int gas] => Moles[gas];
priority: -2
minimumTemperature: 373.149 # Same as Atmospherics.FireMinimumTemperatureToExist
minimumRequirements: # In this case, same as minimum mole count.
- - 0.01 # oxygen
- - 0 # nitrogen
- - 0 # carbon dioxide
- - 0.01 # plasma
+ Oxygen: 0.01
+ Plasma: 0.01
effects:
- !type:PlasmaFireReaction {}
priority: -1
minimumTemperature: 373.149 # Same as Atmospherics.FireMinimumTemperatureToExist
minimumRequirements: # In this case, same as minimum mole count.
- - 0.01 # oxygen
- - 0 # nitrogen
- - 0 # carbon dioxide
- - 0 # plasma
- - 0.01 # tritium
+ Oxygen: 0.01
+ Tritium: 0.01
effects:
- !type:TritiumFireReaction {}
priority: 1
minimumTemperature: 23.15
minimumRequirements:
- - 0 # oxygen
- - 0.01 # nitrogen
- - 0 # carbon dioxide
- - 0 # plasma
- - 0 # tritium
- - 0 # vapor
- - 0 # ammonia
- - 0 # n2o
- - 0.01 # frezon
+ Nitrogen: 0.01
+ Frezon: 0.01
effects:
- !type:FrezonCoolantReaction {}
priority: 2
maximumTemperature: 73.15 # Cold tritium fire, basically.
minimumRequirements:
- - 0.01 # oxygen
- - 0.01 # nitrogen
- - 0 # carbon dioxide
- - 0 # plasma
- - 0.01 # tritium
- - 0 # vapor
- - 0 # ammonia
- - 0 # n2o
- - 0 # frezon
+ Oxygen: 0.01
+ Nitrogen: 0.01
+ Tritium: 0.01
effects:
- !type:FrezonProductionReaction {}
priority: 2
minimumTemperature: 323.149
minimumRequirements:
- - 0.01 # oxygen
- - 0 # nitrogen
- - 0 # carbon dioxide
- - 0 # plasma
- - 0 # tritium
- - 0 # vapor
- - 0.01 # ammonia
- - 0 # n2o
- - 0 # frezon
+ Oxygen: 0.01
+ Ammonia: 0.01
effects:
- !type:AmmoniaOxygenReaction {}
priority: 0
minimumTemperature: 850
minimumRequirements:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # carbon dioxide
- - 0 # plasma
- - 0 # tritium
- - 0 # vapor
- - 0 # ammonia
- - 0.01 # n2o
- - 0 # frezon
+ NitrousOxide: 0.01
effects:
- !type:N2ODecompositionReaction {}
# priority: 1
# maximumTemperature: 373.13 # Boiling point of water.
# minimumRequirements: # In this case, same as minimum mole count.
-# - 0 # oxygen
-# - 0 # nitrogen
-# - 0 # carbon dioxide
-# - 0 # plasma
-# - 0 # tritium
-# - 1 # water vapor
+# WaterVapor: 1
# effects:
# - !type:WaterVaporReaction
# gas: 5
# 31 minutes
volume: 5
moles:
- - 2.051379050 # oxygen
+ Oxygen: 2.051379050 # oxygen
temperature: 293.15
- type: entity
# 4 minutes
volume: 0.66
moles:
- - 0.270782035 # oxygen
+ Oxygen: 0.270782035 # oxygen
temperature: 293.15
- type: entity
# 4 minutes
volume: 0.66
moles:
- - 0 # oxygen
- - 0.270782035 # nitrogen
+ Nitrogen: 0.270782035 # nitrogen
temperature: 293.15
# 9 minutes
volume: 1.5
moles:
- - 0.615413715 # oxygen
+ Oxygen: 0.615413715 # oxygen
temperature: 293.15
- type: entity
# 9 minutes
volume: 1.5
moles:
- - 0 # oxygen
- - 0.615413715 # nitrogen
+ Nitrogen: 0.615413715 # nitrogen
temperature: 293.15
# 15 minutes
volume: 2.5
moles:
- - 1.025689525 # oxygen
+ Oxygen: 1.025689525 # oxygen
temperature: 293.15
- type: entity
# 15 minutes
volume: 2.5
moles:
- - 0 # oxygen
- - 1.025689525 # nitrogen
+ Nitrogen: 1.025689525 # nitrogen
temperature: 293.15
- type: entity
# 4 minutes
volume: 0.66
moles:
- - 0.270782035 # 95% oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # plasma
- - 0 # tritium
- - 0 # water vapor
- - 0 # ammonia
- - 0.014251686 # 5% N2O
- # 0.285033721 total
+ Oxygen: 0.270782035 # 95% oxygen
+ NitrousOxide: 0.014251686 # 5% N2O
+ # 0.285033721 total
temperature: 293.15
- type: entity
# 6 minutes due to output pressure
volume: 5
moles:
- - 0.451303391 # 22% oxygen
- - 1.600075659 # 78% nitrogen
+ Oxygen: 0.451303391 # 22% oxygen
+ Nitrogen: 1.600075659 # 78% nitrogen
# 2.051379050 total
temperature: 293.15
# 31 minutes
volume: 5
moles:
- - 0 # oxygen not included
- - 2.051379050 # nitrogen
+ Nitrogen: 2.051379050 # nitrogen
temperature: 293.15
- type: entity
# only 22 minutes due to pressure
volume: 5
moles:
- - 1.435965335 # 70% oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # plasma
- - 0 # tritium
- - 0 # water vapor
- - 0 # ammonia
- - 0.615413715 # 30% N2O
+ Oxygen: 1.435965335 # 70% oxygen
+ NitrousOxide: 0.615413715 # 30% N2O
# 2.051379050 total
temperature: 293.15
# 6 minutes of agony
volume: 5
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 2.051379050 # plasma
+ Plasma: 2.051379050
temperature: 293.15
volume: 0.75
temperature: 293.15
moles:
- - 0.153853429 # oxygen
- - 0.153853429 # nitrogen
+ Oxygen: 0.153853429 # oxygen
+ Nitrogen: 0.153853429 # nitrogen
- type: Item
sprite: null
size: Normal
gasMixture:
volume: 1000
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 340.5701689 # carbon dioxide
+ CarbonDioxide: 340.5701689 # carbon dioxide
temperature: 373.15
- type: Repairable
doAfterDelay: 30 # you can heal the mothership core, but it takes a while
volume: 5
temperature: 293.15
moles:
- - 1.025689525 # oxygen
- - 1.025689525 # nitrogen
+ Oxygen: 1.025689525 # oxygen
+ Nitrogen: 1.025689525 # nitrogen
#Empty black
- type: entity
volume: 5
temperature: 293.15
moles:
- - 1.025689525 # oxygen
- - 1.025689525 # nitrogen
+ Oxygen: 1.025689525 # oxygen
+ Nitrogen: 1.025689525 # nitrogen
#Empty captain
- type: entity
volume: 5
temperature: 293.15
moles:
- - 1.025689525 # oxygen
- - 1.025689525 # nitrogen
+ Oxygen: 1.025689525 # oxygen
+ Nitrogen: 1.025689525 # nitrogen
#Empty mini
- type: entity
volume: 1.5
temperature: 293.15
moles:
- - 0.307706858 # oxygen
- - 0.307706858 # nitrogen
+ Oxygen: 0.307706858 # oxygen
+ Nitrogen: 0.307706858 # nitrogen
#Empty security
- type: entity
volume: 1.5
temperature: 293.15
moles:
- - 0.307706858 # oxygen
- - 0.307706858 # nitrogen
+ Oxygen: 0.307706858 # oxygen
+ Nitrogen: 0.307706858 # nitrogen
#Empty void
- type: entity
volume: 5
temperature: 293.15
moles:
- - 1.025689525 # oxygen
- - 1.025689525 # nitrogen
+ Oxygen: 1.025689525 # oxygen
+ Nitrogen: 1.025689525 # nitrogen
# Infinite jetpack
- type: entity
air:
volume: 1000
moles: # Target is 3117.84 mols total for filling 30 tiles (goal is 101.325 kPa @ 20C)
- - 654.7464 # oxygen
- - 2463.0936 # nitrogen
+ Oxygen: 654.7464 # oxygen
+ Nitrogen: 2463.0936 # nitrogen
temperature: 293.15
- type: StaticPrice
price: 350
gasMixture:
volume: 1000
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 340.5701689 # carbon dioxide
+ CarbonDioxide: 340.5701689 # carbon dioxide
temperature: 373.15
- type: Explosive
explosionType: Default
- type: GasCanister
gasMixture:
volume: 1500
- moles: # List of gasses for easy reference
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 0 # Tritium
- - 0 # Water vapor
- - 0 # Ammonia
- - 0 # N2O
- - 0 # Frezon
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 581.56 # oxygen 21%
- - 2187.79 # nitrogen 79%
+ Oxygen: 581.56 # oxygen 21%
+ Nitrogen: 2187.79 # nitrogen 79%
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 2769.36 # oxygen
+ Oxygen: 2769.36 # oxygen
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 18710.71051 # oxygen
+ Oxygen: 18710.71051 # oxygen
temperature: 72
- type: AccessReader
access: [["Atmospherics"]]
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 2769.36 # nitrogen
+ Nitrogen: 2769.36 # nitrogen
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 18710.71051 # nitrogen
+ Nitrogen: 18710.71051 # nitrogen
temperature: 72
- type: AccessReader
access: [["Atmospherics"]]
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 2769.36 # CO2
+ CarbonDioxide: 2769.36 # CO2
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 18710.71051 # CO2
+ CarbonDioxide: 18710.71051 # CO2
temperature: 72
- type: AccessReader
access: [["Atmospherics"]]
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # carbon dioxide
- - 2769.36 # plasma
+ Plasma: 2769.36 # plasma
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 2769.36 # Tritium
+ Tritium: 2769.36 # Tritium
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 0 # Tritium
- - 2769.36 # Water vapor
+ WaterVapor: 2769.36 # Water vapor
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 0 # Tritium
- - 0 # Water vapor
- - 2769.36 # Ammonia
+ Ammonia: 2769.36 # Ammonia
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 0 # Tritium
- - 0 # Water vapor
- - 0 # Ammonia
- - 2769.36 # N2O
+ NitrousOxide: 2769.36 # N2O
temperature: 293.15
- type: Destructible
thresholds:
gasMixture:
volume: 1500
moles:
- - 0 # oxygen
- - 0 # nitrogen
- - 0 # CO2
- - 0 # Plasma
- - 0 # Tritium
- - 0 # Water vapor
- - 0 # Ammonia
- - 0 # N2O
- - 2769.36 # Frezon
+ Frezon: 2769.36 # Frezon
temperature: 293.15
- type: Destructible
thresholds: