Add SaveLoadSavePrototype test (#18859)
This commit is contained in:
@@ -30,10 +30,8 @@ namespace Content.IntegrationTests.Tests.Atmos
|
|||||||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
AtmosAlarmThreshold threshold = default!;
|
AtmosAlarmThreshold threshold = default!;
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
var proto = prototypeManager.Index<AtmosAlarmThresholdPrototype>("AlarmThresholdTestDummy");
|
||||||
{
|
threshold = new(proto);
|
||||||
threshold = prototypeManager.Index<AtmosAlarmThreshold>("AlarmThresholdTestDummy");
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ public sealed class PrototypeSaveTest
|
|||||||
SerializationHookContext hookCtx,
|
SerializationHookContext hookCtx,
|
||||||
ISerializationContext? context, ISerializationManager.InstantiationDelegate<EntityUid>? instanceProvider)
|
ISerializationContext? context, ISerializationManager.InstantiationDelegate<EntityUid>? instanceProvider)
|
||||||
{
|
{
|
||||||
return EntityUid.Invalid;
|
return EntityUid.Parse(node.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
#nullable enable
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.Manager;
|
||||||
|
using Robust.Shared.Serialization.Markdown;
|
||||||
using Robust.Shared.Serialization.Markdown.Validation;
|
using Robust.Shared.Serialization.Markdown.Validation;
|
||||||
using Robust.UnitTesting;
|
using Robust.UnitTesting;
|
||||||
|
|
||||||
@@ -59,4 +62,126 @@ public sealed class PrototypeTests
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This test writes all known prototypes as yaml files, reads them again, then serializes them again.
|
||||||
|
/// </summary>
|
||||||
|
[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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This test writes all known prototypes as yaml files, reads them again, then serializes them again.
|
||||||
|
/// </summary>
|
||||||
|
[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<IPrototypeManager>();
|
||||||
|
var seriMan = instance.ResolveDependency<ISerializationManager>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>False if an exception was caught</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Monitor;
|
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;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Monitor.Components;
|
namespace Content.Server.Atmos.Monitor.Components;
|
||||||
|
|
||||||
@@ -19,19 +19,19 @@ public sealed class AtmosMonitorComponent : Component
|
|||||||
// Note that this cancels every single network
|
// Note that this cancels every single network
|
||||||
// event, including ones that may not be
|
// event, including ones that may not be
|
||||||
// related to atmos monitor events.
|
// related to atmos monitor events.
|
||||||
[ViewVariables]
|
[DataField("netEnabled")]
|
||||||
public bool NetEnabled = true;
|
public bool NetEnabled = true;
|
||||||
|
|
||||||
[DataField("temperatureThreshold", customTypeSerializer: (typeof(PrototypeIdSerializer<AtmosAlarmThreshold>)))]
|
[DataField("temperatureThresholdId", customTypeSerializer: (typeof(PrototypeIdSerializer<AtmosAlarmThresholdPrototype>)))]
|
||||||
public readonly string? TemperatureThresholdId;
|
public readonly string? TemperatureThresholdId;
|
||||||
|
|
||||||
[ViewVariables]
|
[DataField("temperatureThreshold")]
|
||||||
public AtmosAlarmThreshold? TemperatureThreshold;
|
public AtmosAlarmThreshold? TemperatureThreshold;
|
||||||
|
|
||||||
[DataField("pressureThreshold", customTypeSerializer: (typeof(PrototypeIdSerializer<AtmosAlarmThreshold>)))]
|
[DataField("pressureThresholdId", customTypeSerializer: (typeof(PrototypeIdSerializer<AtmosAlarmThresholdPrototype>)))]
|
||||||
public readonly string? PressureThresholdId;
|
public readonly string? PressureThresholdId;
|
||||||
|
|
||||||
[ViewVariables]
|
[DataField("pressureThreshold")]
|
||||||
public AtmosAlarmThreshold? PressureThreshold;
|
public AtmosAlarmThreshold? PressureThreshold;
|
||||||
|
|
||||||
// monitor fire - much different from temperature
|
// monitor fire - much different from temperature
|
||||||
@@ -41,14 +41,11 @@ public sealed class AtmosMonitorComponent : Component
|
|||||||
[DataField("monitorFire")]
|
[DataField("monitorFire")]
|
||||||
public bool MonitorFire = false;
|
public bool MonitorFire = false;
|
||||||
|
|
||||||
// really messy but this is parsed at runtime after
|
[DataField("gasThresholdPrototypes",
|
||||||
// prototypes are initialized, there's no
|
customTypeSerializer:typeof(PrototypeIdValueDictionarySerializer<Gas, AtmosAlarmThresholdPrototype>))]
|
||||||
// way without implementing a new
|
public Dictionary<Gas, string>? GasThresholdPrototypes;
|
||||||
// type serializer
|
|
||||||
[DataField("gasThresholds")]
|
|
||||||
public Dictionary<Gas, string>? GasThresholdIds;
|
|
||||||
|
|
||||||
[ViewVariables]
|
[DataField("gasThresholds")]
|
||||||
public Dictionary<Gas, AtmosAlarmThreshold>? GasThresholds;
|
public Dictionary<Gas, AtmosAlarmThreshold>? GasThresholds;
|
||||||
|
|
||||||
// Stores a reference to the gas on the tile this is on.
|
// Stores a reference to the gas on the tile this is on.
|
||||||
@@ -56,14 +53,16 @@ public sealed class AtmosMonitorComponent : Component
|
|||||||
public GasMixture? TileGas;
|
public GasMixture? TileGas;
|
||||||
|
|
||||||
// Stores the last alarm state of this alarm.
|
// Stores the last alarm state of this alarm.
|
||||||
[ViewVariables]
|
[DataField("lastAlarmState")]
|
||||||
public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal;
|
public AtmosAlarmType LastAlarmState = AtmosAlarmType.Normal;
|
||||||
|
|
||||||
[ViewVariables] public HashSet<AtmosMonitorThresholdType> TrippedThresholds = new();
|
[DataField("trippedThresholds")]
|
||||||
|
public HashSet<AtmosMonitorThresholdType> TrippedThresholds = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registered devices in this atmos monitor. Alerts will be sent directly
|
/// Registered devices in this atmos monitor. Alerts will be sent directly
|
||||||
/// to these devices.
|
/// to these devices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables] public HashSet<string> RegisteredDevices = new();
|
[DataField("registeredDevices")]
|
||||||
|
public HashSet<string> RegisteredDevices = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<AtmosMonitorComponent, ComponentInit>(OnAtmosMonitorInit);
|
|
||||||
SubscribeLocalEvent<AtmosMonitorComponent, ComponentStartup>(OnAtmosMonitorStartup);
|
SubscribeLocalEvent<AtmosMonitorComponent, ComponentStartup>(OnAtmosMonitorStartup);
|
||||||
|
SubscribeLocalEvent<AtmosMonitorComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<AtmosMonitorComponent, AtmosDeviceUpdateEvent>(OnAtmosUpdate);
|
SubscribeLocalEvent<AtmosMonitorComponent, AtmosDeviceUpdateEvent>(OnAtmosUpdate);
|
||||||
SubscribeLocalEvent<AtmosMonitorComponent, TileFireEvent>(OnFireEvent);
|
SubscribeLocalEvent<AtmosMonitorComponent, TileFireEvent>(OnFireEvent);
|
||||||
SubscribeLocalEvent<AtmosMonitorComponent, PowerChangedEvent>(OnPowerChangedEvent);
|
SubscribeLocalEvent<AtmosMonitorComponent, PowerChangedEvent>(OnPowerChangedEvent);
|
||||||
@@ -57,23 +57,28 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
atmosMonitor.TileGas = _atmosphereSystem.GetContainingMixture(uid, true);
|
atmosMonitor.TileGas = _atmosphereSystem.GetContainingMixture(uid, true);
|
||||||
}
|
}
|
||||||
|
private void OnMapInit(EntityUid uid, AtmosMonitorComponent component, MapInitEvent args)
|
||||||
private void OnAtmosMonitorInit(EntityUid uid, AtmosMonitorComponent component, ComponentInit args)
|
|
||||||
{
|
{
|
||||||
if (component.TemperatureThresholdId != null)
|
if (component.TemperatureThresholdId != null)
|
||||||
component.TemperatureThreshold = new(_prototypeManager.Index<AtmosAlarmThreshold>(component.TemperatureThresholdId));
|
{
|
||||||
|
var proto = _prototypeManager.Index<AtmosAlarmThresholdPrototype>(component.TemperatureThresholdId);
|
||||||
|
component.TemperatureThreshold ??= new(proto);
|
||||||
|
}
|
||||||
|
|
||||||
if (component.PressureThresholdId != null)
|
if (component.PressureThresholdId != null)
|
||||||
component.PressureThreshold = new(_prototypeManager.Index<AtmosAlarmThreshold>(component.PressureThresholdId));
|
|
||||||
|
|
||||||
if (component.GasThresholdIds != null)
|
|
||||||
{
|
{
|
||||||
component.GasThresholds = new();
|
var proto = _prototypeManager.Index<AtmosAlarmThresholdPrototype>(component.PressureThresholdId);
|
||||||
foreach (var (gas, id) in component.GasThresholdIds)
|
component.PressureThreshold ??= new(proto);
|
||||||
{
|
}
|
||||||
if (_prototypeManager.TryIndex<AtmosAlarmThreshold>(id, out var gasThreshold))
|
|
||||||
component.GasThresholds.Add(gas, new(gasThreshold));
|
if (component.GasThresholdPrototypes == null)
|
||||||
}
|
return;
|
||||||
|
|
||||||
|
component.GasThresholds ??= new();
|
||||||
|
foreach (var (gas, id) in component.GasThresholdPrototypes)
|
||||||
|
{
|
||||||
|
var proto = _prototypeManager.Index<AtmosAlarmThresholdPrototype>(id);
|
||||||
|
component.GasThresholds.TryAdd(gas, new(proto));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Content.Server.DeviceLinking.Components;
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class AutoLinkReceiverComponent : Component
|
public sealed class AutoLinkReceiverComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("channel", required: true, readOnly: true)]
|
[DataField("channel", required: true)]
|
||||||
public string AutoLinkChannel = default!;
|
public string AutoLinkChannel = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Content.Server.DeviceLinking.Components;
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class AutoLinkTransmitterComponent : Component
|
public sealed class AutoLinkTransmitterComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("channel", required: true, readOnly: true)]
|
[DataField("channel", required: true)]
|
||||||
public string AutoLinkChannel = default!;
|
public string AutoLinkChannel = default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace Content.Server.UserInterface
|
|||||||
[DataField("adminOnly")]
|
[DataField("adminOnly")]
|
||||||
public bool AdminOnly { get; set; } = false;
|
public bool AdminOnly { get; set; } = false;
|
||||||
|
|
||||||
[DataField("key", readOnly: true, required: true)]
|
[DataField("key", required: true)]
|
||||||
private string _keyRaw = default!;
|
private string _keyRaw = default!;
|
||||||
|
|
||||||
[DataField("verbText")]
|
[DataField("verbText")]
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ public sealed class IntrinsicUIComponent : Component, ISerializationHooks
|
|||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public struct IntrinsicUIEntry
|
public struct IntrinsicUIEntry
|
||||||
{
|
{
|
||||||
[ViewVariables] public Enum? Key { get; set; } = null;
|
[ViewVariables] public Enum? Key { get; private set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The BUI key that this intrinsic UI should open.
|
/// The BUI key that this intrinsic UI should open.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("key", readOnly: true, required: true)]
|
[DataField("key", required: true)]
|
||||||
private string _keyRaw = default!;
|
private string _keyRaw = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ namespace Content.Server.UserInterface;
|
|||||||
public sealed class OpenUiActionEvent : InstantActionEvent, ISerializationHooks
|
public sealed class OpenUiActionEvent : InstantActionEvent, ISerializationHooks
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[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!;
|
private string _keyRaw = default!;
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
|
|||||||
@@ -3,77 +3,91 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Atmos.Monitor;
|
namespace Content.Shared.Atmos.Monitor;
|
||||||
|
|
||||||
// mostly based around floats and percentages, no literals
|
|
||||||
// except for the range boundaries
|
|
||||||
[Prototype("alarmThreshold")]
|
[Prototype("alarmThreshold")]
|
||||||
[Serializable, NetSerializable]
|
public sealed class AtmosAlarmThresholdPrototype : IPrototype
|
||||||
public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
|
|
||||||
{
|
{
|
||||||
[IdDataField]
|
[IdDataField]
|
||||||
public string ID { get; } = default!;
|
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")]
|
[DataField("ignore")]
|
||||||
public bool Ignore;
|
public bool Ignore;
|
||||||
|
|
||||||
[DataField("upperBound")]
|
[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,
|
// Because the warnings are stored as percentages of the bounds,
|
||||||
// Make a copy of the calculated bounds, so that the real warning amount
|
// Make a copy of the calculated bounds, so that the real warning amount
|
||||||
// doesn't change value when user changes the bounds
|
// doesn't change value when user changes the bounds
|
||||||
var oldWarning = UpperWarningBound;
|
var oldWarning = UpperWarningBound;
|
||||||
_UpperBound = value;
|
_upperBound = value;
|
||||||
UpperWarningBound = oldWarning;
|
UpperWarningBound = oldWarning;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataField("lowerBound")]
|
public AlarmThresholdSetting LowerBound
|
||||||
public AlarmThresholdSetting _LowerBound;
|
{
|
||||||
|
get => _lowerBound;
|
||||||
public AlarmThresholdSetting LowerBound { get { return _LowerBound; } private set
|
set
|
||||||
{
|
{
|
||||||
// Because the warnings are stored as percentages of the bounds,
|
// Because the warnings are stored as percentages of the bounds,
|
||||||
// Make a copy of the calculated bounds, so that the real warning amount
|
// Make a copy of the calculated bounds, so that the real warning amount
|
||||||
// doesn't change value when user changes the bounds
|
// doesn't change value when user changes the bounds
|
||||||
var oldWarning = LowerWarningBound;
|
var oldWarning = LowerWarningBound;
|
||||||
_LowerBound = value;
|
_lowerBound = value;
|
||||||
LowerWarningBound = oldWarning;
|
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]
|
[ViewVariables]
|
||||||
public AlarmThresholdSetting UpperWarningBound
|
public AlarmThresholdSetting UpperWarningBound
|
||||||
{
|
{
|
||||||
get { return CalculateWarningBound(AtmosMonitorThresholdBound.Upper); }
|
get => CalculateWarningBound(AtmosMonitorThresholdBound.Upper);
|
||||||
set { UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value); }
|
set => UpperWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Upper, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public AlarmThresholdSetting LowerWarningBound
|
public AlarmThresholdSetting LowerWarningBound
|
||||||
{
|
{
|
||||||
get { return CalculateWarningBound(AtmosMonitorThresholdBound.Lower); }
|
get => CalculateWarningBound(AtmosMonitorThresholdBound.Lower);
|
||||||
set { LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value); }
|
set => LowerWarningPercentage = CalculateWarningPercentage(AtmosMonitorThresholdBound.Lower, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtmosAlarmThreshold()
|
public AtmosAlarmThreshold()
|
||||||
{
|
{
|
||||||
UpperBound = new AlarmThresholdSetting();
|
|
||||||
LowerBound = new AlarmThresholdSetting();
|
|
||||||
UpperWarningPercentage = new AlarmThresholdSetting();
|
|
||||||
LowerWarningPercentage = new AlarmThresholdSetting();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtmosAlarmThreshold(AtmosAlarmThreshold other)
|
public AtmosAlarmThreshold(AtmosAlarmThreshold other)
|
||||||
@@ -85,12 +99,13 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
|
|||||||
LowerWarningPercentage = other.LowerWarningPercentage;
|
LowerWarningPercentage = other.LowerWarningPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
public AtmosAlarmThreshold(AtmosAlarmThresholdPrototype proto)
|
||||||
{
|
{
|
||||||
UpperBound = new AlarmThresholdSetting{ Enabled = UpperBound.Value != 0, Value = UpperBound.Value };
|
Ignore = proto.Ignore;
|
||||||
LowerBound = new AlarmThresholdSetting{ Enabled = LowerBound.Value != 0, Value = LowerBound.Value };
|
UpperBound = proto.UpperBound;
|
||||||
UpperWarningPercentage = new AlarmThresholdSetting{ Enabled = UpperWarningPercentage.Value != 0, Value = UpperWarningPercentage.Value };
|
LowerBound = proto.LowerBound;
|
||||||
LowerWarningPercentage = new AlarmThresholdSetting{ Enabled = LowerWarningPercentage.Value != 0, Value = LowerWarningPercentage.Value };
|
UpperWarningPercentage = proto.UpperWarningPercentage;
|
||||||
|
LowerWarningPercentage = proto.LowerWarningPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility function to check a threshold against some calculated value
|
// utility function to check a threshold against some calculated value
|
||||||
@@ -238,38 +253,41 @@ public sealed class AtmosAlarmThreshold : IPrototype, ISerializationHooks
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[DataDefinition, Serializable]
|
[DataDefinition, Serializable]
|
||||||
public struct AlarmThresholdSetting
|
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)
|
public static bool operator >=(float a, AlarmThresholdSetting b)
|
||||||
{
|
{
|
||||||
return b.Enabled && a <= b.Value;
|
return b.Enabled && a >= b.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator >=(float a, AlarmThresholdSetting b)
|
public AlarmThresholdSetting WithThreshold(float threshold)
|
||||||
{
|
{
|
||||||
return b.Enabled && a >= b.Value;
|
return this with {Value = threshold};
|
||||||
}
|
}
|
||||||
|
|
||||||
public AlarmThresholdSetting WithThreshold(float threshold)
|
public AlarmThresholdSetting WithEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
return new AlarmThresholdSetting{ Enabled = Enabled, Value = threshold };
|
return this with {Enabled = enabled};
|
||||||
}
|
|
||||||
|
|
||||||
public AlarmThresholdSetting WithEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
return new AlarmThresholdSetting{ Enabled = enabled, Value = Value };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ public sealed class CartridgeLoaderComponent : Component
|
|||||||
[DataField("diskSpace")]
|
[DataField("diskSpace")]
|
||||||
public int DiskSpace = 5;
|
public int DiskSpace = 5;
|
||||||
|
|
||||||
[DataField("uiKey", readOnly: true, required: true, customTypeSerializer: typeof(EnumSerializer))]
|
[DataField("uiKey", required: true, customTypeSerializer: typeof(EnumSerializer))]
|
||||||
public Enum UiKey = default!;
|
public Enum UiKey = default!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ public sealed class DamageSpecifierDictionarySerializer : ITypeReader<Dictionary
|
|||||||
{
|
{
|
||||||
var dict = instanceProvider != null ? instanceProvider() : new();
|
var dict = instanceProvider != null ? instanceProvider() : new();
|
||||||
// Add all the damage types by just copying the type dictionary (if it is not null).
|
// Add all the damage types by just copying the type dictionary (if it is not null).
|
||||||
if (node.TryGet("types", out var typesNode))
|
if (node.TryGet<MappingDataNode>("types", out var typesNode))
|
||||||
{
|
{
|
||||||
serializationManager.Read(typesNode, instanceProvider: () => dict, notNullableOverride: true);
|
serializationManager.Read(typesNode, instanceProvider: () => dict, notNullableOverride: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.TryGet("groups", out var groupsNode))
|
if (!node.TryGet<MappingDataNode>("groups", out var groupsNode))
|
||||||
return dict;
|
return dict;
|
||||||
|
|
||||||
// Then resolve damage groups and add them
|
// Then resolve damage groups and add them
|
||||||
|
|||||||
@@ -248,7 +248,12 @@ namespace Content.Shared.FixedPoint
|
|||||||
|
|
||||||
public void Deserialize(string value)
|
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)}";
|
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
|
||||||
@@ -260,6 +265,11 @@ namespace Content.Shared.FixedPoint
|
|||||||
|
|
||||||
public readonly string Serialize()
|
public readonly string Serialize()
|
||||||
{
|
{
|
||||||
|
// TODO implement "lossless" serializer.
|
||||||
|
// I.e., dont use floats.
|
||||||
|
if (Value == int.MaxValue)
|
||||||
|
return "MaxValue";
|
||||||
|
|
||||||
return ToString();
|
return ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,9 @@
|
|||||||
- type: DeviceNetworkRequiresPower
|
- type: DeviceNetworkRequiresPower
|
||||||
- type: AtmosDevice
|
- type: AtmosDevice
|
||||||
- type: AtmosMonitor
|
- type: AtmosMonitor
|
||||||
temperatureThreshold: stationTemperature
|
temperatureThresholdId: stationTemperature
|
||||||
pressureThreshold: stationPressure
|
pressureThresholdId: stationPressure
|
||||||
gasThresholds:
|
gasThresholdPrototypes:
|
||||||
Oxygen: stationOxygen
|
Oxygen: stationOxygen
|
||||||
Nitrogen: ignore
|
Nitrogen: ignore
|
||||||
CarbonDioxide: stationCO2
|
CarbonDioxide: stationCO2
|
||||||
@@ -127,9 +127,9 @@
|
|||||||
examinableAddress: true
|
examinableAddress: true
|
||||||
- type: DeviceNetworkRequiresPower
|
- type: DeviceNetworkRequiresPower
|
||||||
- type: AtmosMonitor
|
- type: AtmosMonitor
|
||||||
temperatureThreshold: stationTemperature
|
temperatureThresholdId: stationTemperature
|
||||||
pressureThreshold: stationPressure
|
pressureThresholdId: stationPressure
|
||||||
gasThresholds:
|
gasThresholdPrototypes:
|
||||||
Oxygen: stationOxygen
|
Oxygen: stationOxygen
|
||||||
Nitrogen: ignore
|
Nitrogen: ignore
|
||||||
CarbonDioxide: stationCO2
|
CarbonDioxide: stationCO2
|
||||||
|
|||||||
@@ -46,9 +46,9 @@
|
|||||||
- type: DeviceNetworkRequiresPower
|
- type: DeviceNetworkRequiresPower
|
||||||
- type: AtmosDevice
|
- type: AtmosDevice
|
||||||
- type: AtmosMonitor
|
- type: AtmosMonitor
|
||||||
temperatureThreshold: stationTemperature
|
temperatureThresholdId: stationTemperature
|
||||||
pressureThreshold: stationPressure
|
pressureThresholdId: stationPressure
|
||||||
gasThresholds:
|
gasThresholdPrototypes:
|
||||||
Oxygen: stationOxygen
|
Oxygen: stationOxygen
|
||||||
Nitrogen: ignore
|
Nitrogen: ignore
|
||||||
CarbonDioxide: stationCO2
|
CarbonDioxide: stationCO2
|
||||||
|
|||||||
Reference in New Issue
Block a user