diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs index 654a842084..9ebc319d43 100644 --- a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs @@ -30,10 +30,8 @@ namespace Content.IntegrationTests.Tests.Atmos var prototypeManager = server.ResolveDependency(); AtmosAlarmThreshold threshold = default!; - await server.WaitPost(() => - { - threshold = prototypeManager.Index("AlarmThresholdTestDummy"); - }); + var proto = prototypeManager.Index("AlarmThresholdTestDummy"); + threshold = new(proto); await server.WaitAssertion(() => { diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 1213d652e0..74123b5bc7 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -232,7 +232,7 @@ public sealed class PrototypeSaveTest SerializationHookContext hookCtx, ISerializationContext? context, ISerializationManager.InstantiationDelegate? instanceProvider) { - return EntityUid.Invalid; + return EntityUid.Parse(node.Value); } } } diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs index 77f60800fc..8b7c73380e 100644 --- a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs @@ -1,5 +1,8 @@ +#nullable enable using System.Collections.Generic; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; using Robust.Shared.Serialization.Markdown.Validation; using Robust.UnitTesting; @@ -59,4 +62,126 @@ public sealed class PrototypeTests } }); } + + /// + /// This test writes all known prototypes as yaml files, reads them again, then serializes them again. + /// + [Test] + public async Task ServerPrototypeSaveLoadSaveTest() + { + await using var pairTracker = await PoolManager.GetServerClient(); + var context = new PrototypeSaveTest.TestEntityUidContext(); + await SaveLoadSavePrototype(pairTracker.Pair.Server, context); + await pairTracker.CleanReturnAsync(); + } + + /// + /// This test writes all known prototypes as yaml files, reads them again, then serializes them again. + /// + [Test] + public async Task ClientPrototypeSaveLoadSaveTest() + { + await using var pairTracker = await PoolManager.GetServerClient(); + var context = new PrototypeSaveTest.TestEntityUidContext(); + await SaveLoadSavePrototype(pairTracker.Pair.Client, context); + await pairTracker.CleanReturnAsync(); + } + + private async Task SaveLoadSavePrototype( + RobustIntegrationTest.IntegrationInstance instance, + PrototypeSaveTest.TestEntityUidContext ctx) + { + var protoMan = instance.ResolveDependency(); + var seriMan = instance.ResolveDependency(); + await instance.WaitAssertion(() => + { + Assert.Multiple(() => + { + foreach (var kind in protoMan.EnumeratePrototypeKinds()) + { + foreach (var proto in protoMan.EnumeratePrototypes(kind)) + { + var noException = TrySaveLoadSavePrototype( + seriMan, + protoMan, + kind, + proto, + ctx); + + // This will probably throw an exception for each prototype of this kind. + // We want to avoid having tests crash because they run out of time. + if (!noException) + break; + } + } + }); + }); + } + + /// False if an exception was caught + private bool TrySaveLoadSavePrototype( + ISerializationManager seriMan, + IPrototypeManager protoMan, + Type kind, + IPrototype proto, + PrototypeSaveTest.TestEntityUidContext ctx) + { + DataNode first; + DataNode second; + + try + { + first = seriMan.WriteValue(kind, proto, alwaysWrite: true, context:ctx); + } + catch (Exception e) + { + protoMan.TryGetMapping(kind, proto.ID, out var mapping); + Assert.Fail($"Caught exception while writing {kind.Name} prototype {proto.ID}. Exception:\n{e}"); + return false; + } + + object? obj; + try + { + obj = seriMan.Read(kind, first, context:ctx); + } + catch (Exception e) + { + protoMan.TryGetMapping(kind, proto.ID, out var mapping); + Assert.Fail($"Caught exception while re-reading {kind.Name} prototype {proto.ID}." + + $"\nException:\n{e}" + + $"\n\nOriginal yaml:\n{mapping}" + + $"\n\nWritten yaml:\n{first}"); + return false; + } + + Assert.That(obj?.GetType(), Is.EqualTo(proto.GetType())); + var deserialized = (IPrototype) obj!; + + try + { + second = seriMan.WriteValue(kind, deserialized, alwaysWrite: true, context:ctx); + } + catch (Exception e) + { + protoMan.TryGetMapping(kind, proto.ID, out var mapping); + Assert.Fail($"Caught exception while re-writing {kind.Name} prototype {proto.ID}." + + $"\nException:\n{e}" + + $"\n\nOriginal yaml:\n{mapping}" + + $"\n\nWritten yaml:\n{first}"); + return false; + } + + var diff = first.Except(second); + if (diff == null || diff.IsEmpty) + return true; + + protoMan.TryGetMapping(kind, proto.ID, out var orig); + Assert.Fail($"Re-written {kind.Name} prototype {proto.ID} differs." + + $"\nYaml diff:\n{diff}" + + $"\n\nOriginal yaml:\n{orig}" + + $"\n\nWritten yaml:\n{first}" + + $"\n\nRe-written Yaml:\n{second}"); + return true; + } } diff --git a/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs b/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs index a229544d03..d5055e19e8 100644 --- a/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs +++ b/Content.Server/Atmos/Monitor/Components/AtmosMonitorComponent.cs @@ -1,7 +1,7 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; -using Robust.Shared.Audio; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Server.Atmos.Monitor.Components; @@ -19,19 +19,19 @@ public sealed class AtmosMonitorComponent : Component // Note that this cancels every single network // event, including ones that may not be // related to atmos monitor events. - [ViewVariables] + [DataField("netEnabled")] public bool NetEnabled = true; - [DataField("temperatureThreshold", customTypeSerializer: (typeof(PrototypeIdSerializer)))] + [DataField("temperatureThresholdId", customTypeSerializer: (typeof(PrototypeIdSerializer)))] public readonly string? TemperatureThresholdId; - [ViewVariables] + [DataField("temperatureThreshold")] public AtmosAlarmThreshold? TemperatureThreshold; - [DataField("pressureThreshold", customTypeSerializer: (typeof(PrototypeIdSerializer)))] + [DataField("pressureThresholdId", customTypeSerializer: (typeof(PrototypeIdSerializer)))] public readonly string? PressureThresholdId; - [ViewVariables] + [DataField("pressureThreshold")] public AtmosAlarmThreshold? PressureThreshold; // monitor fire - much different from temperature @@ -41,14 +41,11 @@ public sealed class AtmosMonitorComponent : Component [DataField("monitorFire")] public bool MonitorFire = false; - // really messy but this is parsed at runtime after - // prototypes are initialized, there's no - // way without implementing a new - // type serializer - [DataField("gasThresholds")] - public Dictionary? GasThresholdIds; + [DataField("gasThresholdPrototypes", + customTypeSerializer:typeof(PrototypeIdValueDictionarySerializer))] + public Dictionary? GasThresholdPrototypes; - [ViewVariables] + [DataField("gasThresholds")] public Dictionary? GasThresholds; // Stores a reference to the gas on the tile this is on. @@ -56,14 +53,16 @@ public sealed class AtmosMonitorComponent : Component public GasMixture? TileGas; // Stores the last alarm state of this alarm. - [ViewVariables] + [DataField("lastAlarmState")] public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal; - [ViewVariables] public HashSet TrippedThresholds = new(); + [DataField("trippedThresholds")] + public HashSet TrippedThresholds = new(); /// /// Registered devices in this atmos monitor. Alerts will be sent directly /// to these devices. /// - [ViewVariables] public HashSet RegisteredDevices = new(); + [DataField("registeredDevices")] + public HashSet RegisteredDevices = new(); } diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index 528c7289da..c7995f4ae6 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -37,8 +37,8 @@ public sealed class AtmosMonitorSystem : EntitySystem public override void Initialize() { - SubscribeLocalEvent(OnAtmosMonitorInit); SubscribeLocalEvent(OnAtmosMonitorStartup); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnAtmosUpdate); SubscribeLocalEvent(OnFireEvent); SubscribeLocalEvent(OnPowerChangedEvent); @@ -57,23 +57,28 @@ public sealed class AtmosMonitorSystem : EntitySystem { atmosMonitor.TileGas = _atmosphereSystem.GetContainingMixture(uid, true); } - - private void OnAtmosMonitorInit(EntityUid uid, AtmosMonitorComponent component, ComponentInit args) + private void OnMapInit(EntityUid uid, AtmosMonitorComponent component, MapInitEvent args) { if (component.TemperatureThresholdId != null) - component.TemperatureThreshold = new(_prototypeManager.Index(component.TemperatureThresholdId)); + { + var proto = _prototypeManager.Index(component.TemperatureThresholdId); + component.TemperatureThreshold ??= new(proto); + } if (component.PressureThresholdId != null) - component.PressureThreshold = new(_prototypeManager.Index(component.PressureThresholdId)); - - if (component.GasThresholdIds != null) { - component.GasThresholds = new(); - foreach (var (gas, id) in component.GasThresholdIds) - { - if (_prototypeManager.TryIndex(id, out var gasThreshold)) - component.GasThresholds.Add(gas, new(gasThreshold)); - } + var proto = _prototypeManager.Index(component.PressureThresholdId); + component.PressureThreshold ??= new(proto); + } + + if (component.GasThresholdPrototypes == null) + return; + + component.GasThresholds ??= new(); + foreach (var (gas, id) in component.GasThresholdPrototypes) + { + var proto = _prototypeManager.Index(id); + component.GasThresholds.TryAdd(gas, new(proto)); } } diff --git a/Content.Server/DeviceLinking/Components/AutoLinkReceiverComponent.cs b/Content.Server/DeviceLinking/Components/AutoLinkReceiverComponent.cs index 9a98f81e31..6bf8c7fec5 100644 --- a/Content.Server/DeviceLinking/Components/AutoLinkReceiverComponent.cs +++ b/Content.Server/DeviceLinking/Components/AutoLinkReceiverComponent.cs @@ -6,7 +6,7 @@ namespace Content.Server.DeviceLinking.Components; [RegisterComponent] public sealed class AutoLinkReceiverComponent : Component { - [DataField("channel", required: true, readOnly: true)] + [DataField("channel", required: true)] public string AutoLinkChannel = default!; } diff --git a/Content.Server/DeviceLinking/Components/AutoLinkTransmitterComponent.cs b/Content.Server/DeviceLinking/Components/AutoLinkTransmitterComponent.cs index b013d9f578..f42628dff6 100644 --- a/Content.Server/DeviceLinking/Components/AutoLinkTransmitterComponent.cs +++ b/Content.Server/DeviceLinking/Components/AutoLinkTransmitterComponent.cs @@ -6,7 +6,7 @@ namespace Content.Server.DeviceLinking.Components; [RegisterComponent] public sealed class AutoLinkTransmitterComponent : Component { - [DataField("channel", required: true, readOnly: true)] + [DataField("channel", required: true)] public string AutoLinkChannel = default!; } diff --git a/Content.Server/UserInterface/ActivatableUIComponent.cs b/Content.Server/UserInterface/ActivatableUIComponent.cs index 5116331456..f1dcb80382 100644 --- a/Content.Server/UserInterface/ActivatableUIComponent.cs +++ b/Content.Server/UserInterface/ActivatableUIComponent.cs @@ -25,7 +25,7 @@ namespace Content.Server.UserInterface [DataField("adminOnly")] public bool AdminOnly { get; set; } = false; - [DataField("key", readOnly: true, required: true)] + [DataField("key", required: true)] private string _keyRaw = default!; [DataField("verbText")] diff --git a/Content.Server/UserInterface/IntrinsicUIComponent.cs b/Content.Server/UserInterface/IntrinsicUIComponent.cs index ce344b9686..dd6fcdbd90 100644 --- a/Content.Server/UserInterface/IntrinsicUIComponent.cs +++ b/Content.Server/UserInterface/IntrinsicUIComponent.cs @@ -27,12 +27,12 @@ public sealed class IntrinsicUIComponent : Component, ISerializationHooks [DataDefinition] public struct IntrinsicUIEntry { - [ViewVariables] public Enum? Key { get; set; } = null; + [ViewVariables] public Enum? Key { get; private set; } = null; /// /// The BUI key that this intrinsic UI should open. /// - [DataField("key", readOnly: true, required: true)] + [DataField("key", required: true)] private string _keyRaw = default!; /// diff --git a/Content.Server/UserInterface/OpenUiActionEvent.cs b/Content.Server/UserInterface/OpenUiActionEvent.cs index a6942f0e08..59bb4ac9d1 100644 --- a/Content.Server/UserInterface/OpenUiActionEvent.cs +++ b/Content.Server/UserInterface/OpenUiActionEvent.cs @@ -7,9 +7,9 @@ namespace Content.Server.UserInterface; public sealed class OpenUiActionEvent : InstantActionEvent, ISerializationHooks { [ViewVariables] - public Enum? Key { get; set; } + public Enum? Key { get; private set; } - [DataField("key", readOnly: true, required: true)] + [DataField("key", required: true)] private string _keyRaw = default!; void ISerializationHooks.AfterDeserialization() diff --git a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs index af4f9b850e..1a9fb71a05 100644 --- a/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs +++ b/Content.Shared/Atmos/Monitor/AtmosAlarmThreshold.cs @@ -3,77 +3,91 @@ using Robust.Shared.Serialization; namespace Content.Shared.Atmos.Monitor; -// mostly based around floats and percentages, no literals -// except for the range boundaries + [Prototype("alarmThreshold")] -[Serializable, NetSerializable] -public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks +public sealed class AtmosAlarmThresholdPrototype : IPrototype { [IdDataField] public string ID { get; } = default!; + + [DataField("ignore")] + public readonly bool Ignore; + + [DataField("upperBound")] + public readonly AlarmThresholdSetting UpperBound = AlarmThresholdSetting.Disabled; + + [DataField("lowerBound")] + public readonly AlarmThresholdSetting LowerBound = AlarmThresholdSetting.Disabled; + + [DataField("upperWarnAround")] + public readonly AlarmThresholdSetting UpperWarningPercentage = AlarmThresholdSetting.Disabled; + + [DataField("lowerWarnAround")] + public readonly AlarmThresholdSetting LowerWarningPercentage = AlarmThresholdSetting.Disabled; +} + +[Serializable, NetSerializable, DataDefinition] +public sealed class AtmosAlarmThreshold +{ [DataField("ignore")] public bool Ignore; [DataField("upperBound")] - private AlarmThresholdSetting _UpperBound; + private AlarmThresholdSetting _upperBound = AlarmThresholdSetting.Disabled; - public AlarmThresholdSetting UpperBound { get { return _UpperBound; } private set + [DataField("lowerBound")] + private AlarmThresholdSetting _lowerBound = AlarmThresholdSetting.Disabled; + + [DataField("upperWarnAround")] + public AlarmThresholdSetting UpperWarningPercentage = AlarmThresholdSetting.Disabled; + + [DataField("lowerWarnAround")] + public AlarmThresholdSetting LowerWarningPercentage = AlarmThresholdSetting.Disabled; + + public AlarmThresholdSetting UpperBound + { + get => _upperBound; + set { // Because the warnings are stored as percentages of the bounds, // Make a copy of the calculated bounds, so that the real warning amount // doesn't change value when user changes the bounds var oldWarning = UpperWarningBound; - _UpperBound = value; + _upperBound = value; UpperWarningBound = oldWarning; } } - [DataField("lowerBound")] - public AlarmThresholdSetting _LowerBound; - - public AlarmThresholdSetting LowerBound { get { return _LowerBound; } private set + public AlarmThresholdSetting LowerBound + { + get => _lowerBound; + set { // Because the warnings are stored as percentages of the bounds, // Make a copy of the calculated bounds, so that the real warning amount // doesn't change value when user changes the bounds var oldWarning = LowerWarningBound; - _LowerBound = value; + _lowerBound = value; LowerWarningBound = oldWarning; } } - // upper warning percentage - // must always cause UpperWarningBound - // to be smaller - [DataField("upperWarnAround")] - public AlarmThresholdSetting UpperWarningPercentage { get; private set; } - - // lower warning percentage - // must always cause LowerWarningBound - // to be larger - [DataField("lowerWarnAround")] - public AlarmThresholdSetting LowerWarningPercentage { get; private set; } - [ViewVariables] public AlarmThresholdSetting UpperWarningBound { - get { return CalculateWarningBound(AtmosMonitorThresholdBound.Upper); } - set { UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value); } + get => CalculateWarningBound(AtmosMonitorThresholdBound.Upper); + set => UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value); } [ViewVariables] public AlarmThresholdSetting LowerWarningBound { - get { return CalculateWarningBound(AtmosMonitorThresholdBound.Lower); } - set { LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value); } + get => CalculateWarningBound(AtmosMonitorThresholdBound.Lower); + set => LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value); } public AtmosAlarmThreshold() { - UpperBound = new AlarmThresholdSetting(); - LowerBound = new AlarmThresholdSetting(); - UpperWarningPercentage = new AlarmThresholdSetting(); - LowerWarningPercentage = new AlarmThresholdSetting(); } public AtmosAlarmThreshold(AtmosAlarmThreshold other) @@ -85,12 +99,13 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks LowerWarningPercentage = other.LowerWarningPercentage; } - void ISerializationHooks.AfterDeserialization() + public AtmosAlarmThreshold(AtmosAlarmThresholdPrototype proto) { - UpperBound = new AlarmThresholdSetting{ Enabled = UpperBound.Value != 0, Value = UpperBound.Value }; - LowerBound = new AlarmThresholdSetting{ Enabled = LowerBound.Value != 0, Value = LowerBound.Value }; - UpperWarningPercentage = new AlarmThresholdSetting{ Enabled = UpperWarningPercentage.Value != 0, Value = UpperWarningPercentage.Value }; - LowerWarningPercentage = new AlarmThresholdSetting{ Enabled = LowerWarningPercentage.Value != 0, Value = LowerWarningPercentage.Value }; + Ignore = proto.Ignore; + UpperBound = proto.UpperBound; + LowerBound = proto.LowerBound; + UpperWarningPercentage = proto.UpperWarningPercentage; + LowerWarningPercentage = proto.LowerWarningPercentage; } // utility function to check a threshold against some calculated value @@ -238,38 +253,41 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks break; } } +} - [DataDefinition, Serializable] - public struct AlarmThresholdSetting +[DataDefinition, Serializable] +public readonly struct AlarmThresholdSetting +{ + [DataField("enabled")] + public bool Enabled { get; init; } = true; + + [DataField("threshold")] + public float Value { get; init; } = 1; + + public static AlarmThresholdSetting Disabled = new() {Enabled = false, Value = 0}; + + public AlarmThresholdSetting() { - [DataField("enabled")] - public bool Enabled { get; set; } = false; - [DataField("threshold")] - public float Value { get; set; } = 0; + } - public AlarmThresholdSetting() - { - } + public static bool operator <=(float a, AlarmThresholdSetting b) + { + return b.Enabled && a <= b.Value; + } - public static bool operator <=(float a, AlarmThresholdSetting b) - { - return b.Enabled && a <= b.Value; - } + public static bool operator >=(float a, AlarmThresholdSetting b) + { + return b.Enabled && a >= b.Value; + } - public static bool operator >=(float a, AlarmThresholdSetting b) - { - return b.Enabled && a >= b.Value; - } + public AlarmThresholdSetting WithThreshold(float threshold) + { + return this with {Value = threshold}; + } - public AlarmThresholdSetting WithThreshold(float threshold) - { - return new AlarmThresholdSetting{ Enabled = Enabled, Value = threshold }; - } - - public AlarmThresholdSetting WithEnabled(bool enabled) - { - return new AlarmThresholdSetting{ Enabled = enabled, Value = Value }; - } + public AlarmThresholdSetting WithEnabled(bool enabled) + { + return this with {Enabled = enabled}; } } diff --git a/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs b/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs index 0fa946b248..2822c76b28 100644 --- a/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs +++ b/Content.Shared/CartridgeLoader/CartridgeLoaderComponent.cs @@ -42,6 +42,6 @@ public sealed class CartridgeLoaderComponent : Component [DataField("diskSpace")] public int DiskSpace = 5; - [DataField("uiKey", readOnly: true, required: true, customTypeSerializer: typeof(EnumSerializer))] + [DataField("uiKey", required: true, customTypeSerializer: typeof(EnumSerializer))] public Enum UiKey = default!; } diff --git a/Content.Shared/Damage/DamageSpecifierDictionarySerializer.cs b/Content.Shared/Damage/DamageSpecifierDictionarySerializer.cs index e6ef48bdb0..92f56571a6 100644 --- a/Content.Shared/Damage/DamageSpecifierDictionarySerializer.cs +++ b/Content.Shared/Damage/DamageSpecifierDictionarySerializer.cs @@ -39,12 +39,12 @@ public sealed class DamageSpecifierDictionarySerializer : ITypeReader("types", out var typesNode)) { serializationManager.Read(typesNode, instanceProvider: () => dict, notNullableOverride: true); } - if (!node.TryGet("groups", out var groupsNode)) + if (!node.TryGet("groups", out var groupsNode)) return dict; // Then resolve damage groups and add them diff --git a/Content.Shared/FixedPoint/FixedPoint2.cs b/Content.Shared/FixedPoint/FixedPoint2.cs index 2bce52e6cb..e296c276f7 100644 --- a/Content.Shared/FixedPoint/FixedPoint2.cs +++ b/Content.Shared/FixedPoint/FixedPoint2.cs @@ -248,7 +248,12 @@ namespace Content.Shared.FixedPoint public void Deserialize(string value) { - Value = FromFloat(FloatFromString(value)); + // TODO implement "lossless" serializer. + // I.e., dont use floats. + if (value == "MaxValue") + Value = int.MaxValue; + else + Value = FromFloat(FloatFromString(value)); } public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}"; @@ -260,6 +265,11 @@ namespace Content.Shared.FixedPoint public readonly string Serialize() { + // TODO implement "lossless" serializer. + // I.e., dont use floats. + if (Value == int.MaxValue) + return "MaxValue"; + return ToString(); } diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index 7e6f033547..4745a5c7eb 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -38,9 +38,9 @@ - type: DeviceNetworkRequiresPower - type: AtmosDevice - type: AtmosMonitor - temperatureThreshold: stationTemperature - pressureThreshold: stationPressure - gasThresholds: + temperatureThresholdId: stationTemperature + pressureThresholdId: stationPressure + gasThresholdPrototypes: Oxygen: stationOxygen Nitrogen: ignore CarbonDioxide: stationCO2 @@ -127,9 +127,9 @@ examinableAddress: true - type: DeviceNetworkRequiresPower - type: AtmosMonitor - temperatureThreshold: stationTemperature - pressureThreshold: stationPressure - gasThresholds: + temperatureThresholdId: stationTemperature + pressureThresholdId: stationPressure + gasThresholdPrototypes: Oxygen: stationOxygen Nitrogen: ignore CarbonDioxide: stationCO2 diff --git a/Resources/Prototypes/Entities/Structures/Specific/Atmospherics/sensor.yml b/Resources/Prototypes/Entities/Structures/Specific/Atmospherics/sensor.yml index 727e88d70f..6fa085646a 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Atmospherics/sensor.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Atmospherics/sensor.yml @@ -46,9 +46,9 @@ - type: DeviceNetworkRequiresPower - type: AtmosDevice - type: AtmosMonitor - temperatureThreshold: stationTemperature - pressureThreshold: stationPressure - gasThresholds: + temperatureThresholdId: stationTemperature + pressureThresholdId: stationPressure + gasThresholdPrototypes: Oxygen: stationOxygen Nitrogen: ignore CarbonDioxide: stationCO2