Prevent infinite loops in device linking (#16856)
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
using Content.Server.DeviceLinking.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components.Overload;
|
||||
|
||||
/// <summary>
|
||||
/// Plays a sound when a device link overloads.
|
||||
/// An overload happens when a device link sink is invoked to many times per tick
|
||||
/// and it raises a <see cref="Content.Server.DeviceLinking.Events.DeviceLinkOverloadedEvent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DeviceLinkOverloadSystem))]
|
||||
public sealed class SoundOnOverloadComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sound to play when the device overloads
|
||||
/// </summary>
|
||||
[DataField("sound")]
|
||||
public SoundSpecifier? OverloadSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_zap.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the volume the sound is played at
|
||||
/// </summary>
|
||||
[DataField("volumeModifier")]
|
||||
public float VolumeModifier;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.DeviceLinking.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components.Overload;
|
||||
|
||||
/// <summary>
|
||||
/// Spawns an entity when a device link overloads.
|
||||
/// An overload happens when a device link sink is invoked to many times per tick
|
||||
/// and it raises a <see cref="Content.Server.DeviceLinking.Events.DeviceLinkOverloadedEvent"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(DeviceLinkOverloadSystem))]
|
||||
public sealed class SpawnOnOverloadComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity prototype to spawn when the device overloads
|
||||
/// </summary>
|
||||
[DataField("spawnedPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = "PuddleSparkle";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Server.DeviceLinking.Events;
|
||||
|
||||
[ByRefEvent]
|
||||
public readonly record struct DeviceLinkOverloadedEvent;
|
||||
@@ -0,0 +1,29 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceLinking.Components.Overload;
|
||||
using Content.Server.DeviceLinking.Events;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Systems;
|
||||
|
||||
public sealed class DeviceLinkOverloadSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<SoundOnOverloadComponent, DeviceLinkOverloadedEvent>(OnOverloadSound);
|
||||
SubscribeLocalEvent<SpawnOnOverloadComponent, DeviceLinkOverloadedEvent>(OnOverloadSpawn);
|
||||
}
|
||||
|
||||
private void OnOverloadSound(EntityUid uid, SoundOnOverloadComponent component, ref DeviceLinkOverloadedEvent args)
|
||||
{
|
||||
|
||||
_audioSystem.PlayPvs(component.OverloadSound, uid, AudioParams.Default.WithVolume(component.VolumeModifier));
|
||||
}
|
||||
|
||||
|
||||
private void OnOverloadSpawn(EntityUid uid, SpawnOnOverloadComponent component, ref DeviceLinkOverloadedEvent args)
|
||||
{
|
||||
Spawn(component.Prototype, Transform(uid).Coordinates);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,23 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
|
||||
SubscribeLocalEvent<DeviceLinkSinkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<DeviceLinkSinkComponent>();
|
||||
|
||||
while (query.MoveNext(out var component))
|
||||
{
|
||||
if (component.InvokeLimit < 1)
|
||||
{
|
||||
component.InvokeCounter = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(component.InvokeCounter > 0)
|
||||
component.InvokeCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves existing links from machine linking to device linking to ensure linked things still work even when the map wasn't updated yet
|
||||
/// </summary>
|
||||
@@ -62,11 +79,25 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
|
||||
if (!sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var links))
|
||||
continue;
|
||||
|
||||
if (!TryComp<DeviceLinkSinkComponent>(sinkUid, out var sinkComponent))
|
||||
continue;
|
||||
|
||||
foreach (var (source, sink) in links)
|
||||
{
|
||||
if (source != port)
|
||||
continue;
|
||||
|
||||
if (sinkComponent.InvokeCounter > sinkComponent.InvokeLimit)
|
||||
{
|
||||
sinkComponent.InvokeCounter = 0;
|
||||
var args = new DeviceLinkOverloadedEvent();
|
||||
RaiseLocalEvent(sinkUid, ref args);
|
||||
RemoveAllFromSink(sinkUid, sinkComponent);
|
||||
continue;
|
||||
}
|
||||
|
||||
sinkComponent.InvokeCounter++;
|
||||
|
||||
//Just skip using device networking if the source or the sink doesn't support it
|
||||
if (!HasComp<DeviceNetworkComponent>(uid) || !TryComp<DeviceNetworkComponent?>(sinkUid, out var sinkNetworkComponent))
|
||||
{
|
||||
@@ -109,6 +140,4 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
|
||||
RaiseLocalEvent(uid, ref eventArgs);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
{
|
||||
if (state == SignalState.High || state == SignalState.Momentary)
|
||||
{
|
||||
if (door.State != DoorState.Open)
|
||||
if (door.State == DoorState.Closed)
|
||||
_doorSystem.TryOpen(uid, door);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
{
|
||||
if (state == SignalState.High || state == SignalState.Momentary)
|
||||
{
|
||||
if (door.State != DoorState.Closed)
|
||||
if (door.State == DoorState.Open)
|
||||
_doorSystem.TryClose(uid, door);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,8 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
{
|
||||
if (state == SignalState.High || state == SignalState.Momentary)
|
||||
{
|
||||
_doorSystem.TryToggleDoor(uid, door);
|
||||
if (door.State is DoorState.Closed or DoorState.Open)
|
||||
_doorSystem.TryToggleDoor(uid, door);
|
||||
}
|
||||
}
|
||||
else if (args.Port == component.InBolt)
|
||||
|
||||
Reference in New Issue
Block a user