From 2bb1fde4dcfcd9c4da4b771288b0c2159c79f35a Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Fri, 26 May 2023 00:08:22 +0200 Subject: [PATCH] Remove devices from device lists when they get deleted (#16783) * Implement device network device shutdown subscribtion Implement removing devices from device lists when they get deleted * Improve name and doc comment for DeviceShutDownEvent * Change ShutdownSubscriber data field tag * Change UpdateRemovalSubscription name to UpdateShutdownSubscription --- .../Systems/DeviceListSystem.cs | 1 + .../Atmos/Monitor/Systems/AirAlarmSystem.cs | 1 + .../Atmos/Monitor/Systems/FireAlarmSystem.cs | 1 + .../Components/DeviceNetworkComponent.cs | 8 + .../DeviceNetwork/Systems/DeviceListSystem.cs | 154 ++++++++++-------- .../Systems/DeviceNetworkSystem.cs | 48 ++++++ .../Components/DeviceListComponent.cs | 3 +- .../Systems/SharedDeviceListSystem.cs | 8 +- 8 files changed, 157 insertions(+), 67 deletions(-) diff --git a/Content.Client/NetworkConfigurator/Systems/DeviceListSystem.cs b/Content.Client/NetworkConfigurator/Systems/DeviceListSystem.cs index 150361c0ae..0b9fd6db05 100644 --- a/Content.Client/NetworkConfigurator/Systems/DeviceListSystem.cs +++ b/Content.Client/NetworkConfigurator/Systems/DeviceListSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Systems; namespace Content.Client.NetworkConfigurator.Systems; diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index 8f6cf55dec..7eedad2c37 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Systems; using Content.Shared.Interaction; using Content.Shared.Wires; using Robust.Server.GameObjects; diff --git a/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs index eca7cc86fa..0a3ee4d7f7 100644 --- a/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.AlertLevel; using Content.Shared.Atmos.Monitor; using Content.Shared.CCVar; using Content.Shared.DeviceNetwork; +using Content.Shared.DeviceNetwork.Systems; using Content.Shared.Interaction; using Content.Shared.Emag.Systems; using Robust.Server.GameObjects; diff --git a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs b/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs index a50e6c4259..55b9170422 100644 --- a/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs +++ b/Content.Server/DeviceNetwork/Components/DeviceNetworkComponent.cs @@ -101,5 +101,13 @@ namespace Content.Server.DeviceNetwork.Components [ViewVariables(VVAccess.ReadWrite)] [DataField("sendBroadcastAttemptEvent")] public bool SendBroadcastAttemptEvent = false; + + /// + /// A list of entities that get sent the when this entity gets deleted.

+ /// When a device subscribes to the deletion of another device the entity id of the device being subscribed + /// to also gets saved on the subscribing device. + ///
+ [DataField("ShutdownSubscribers")] + public HashSet ShutdownSubscribers = new(); } } diff --git a/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs index edc65764da..1869db2998 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs @@ -2,6 +2,7 @@ using Content.Server.DeviceNetwork.Components; using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Components; +using Content.Shared.DeviceNetwork.Systems; using Content.Shared.Interaction; using JetBrains.Annotations; using Robust.Shared.Map.Events; @@ -13,16 +14,104 @@ public sealed class DeviceListSystem : SharedDeviceListSystem { private ISawmill _sawmill = default!; + [Dependency] private DeviceNetworkSystem _deviceNetworkSystem = null!; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnBeforeBroadcast); SubscribeLocalEvent(OnBeforePacketSent); + SubscribeLocalEvent(OnDeviceShutdown); SubscribeLocalEvent(OnMapSave); _sawmill = Logger.GetSawmill("devicelist"); } + public void OnInit(EntityUid uid, DeviceListComponent component, ComponentInit args) + { + Dirty(component); + } + + /// + /// Gets the given device list as a dictionary + /// + /// + /// If any entity in the device list is pre-map init, it will show the entity UID of the device instead. + /// + public Dictionary GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null) + { + if (!Resolve(uid, ref deviceList)) + return new Dictionary(); + + var devices = new Dictionary(deviceList.Devices.Count); + + foreach (var deviceUid in deviceList.Devices) + { + if (!TryComp(deviceUid, out DeviceNetworkComponent? deviceNet)) + continue; + + var address = MetaData(deviceUid).EntityLifeStage == EntityLifeStage.MapInitialized + ? deviceNet.Address + : $"UID: {deviceUid.ToString()}"; + + devices.Add(address, deviceUid); + + } + + return devices; + } + + protected override void UpdateShutdownSubscription(EntityUid uid, List newDevices, List oldDevices) + { + foreach (var device in newDevices) + { + _deviceNetworkSystem.SubscribeToDeviceShutdown(uid, device); + } + + var removedDevices = oldDevices.Except(newDevices); + foreach (var device in removedDevices) + { + _deviceNetworkSystem.UnsubscribeFromDeviceShutdown(uid, device); + } + } + + /// + /// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field + /// + private void OnBeforeBroadcast(EntityUid uid, DeviceListComponent component, BeforeBroadcastAttemptEvent args) + { + //Don't filter anything if the device list is empty + if (component.Devices.Count == 0) + { + if (component.IsAllowList) args.Cancel(); + return; + } + + HashSet filteredRecipients = new(args.Recipients.Count); + + foreach (var recipient in args.Recipients) + { + if (component.Devices.Contains(recipient.Owner) == component.IsAllowList) filteredRecipients.Add(recipient); + } + + args.ModifiedRecipients = filteredRecipients; + } + + /// + /// Filters incoming packets if that is enabled + /// + private void OnBeforePacketSent(EntityUid uid, DeviceListComponent component, BeforePacketSentEvent args) + { + if (component.HandleIncomingPackets && component.Devices.Contains(args.Sender) != component.IsAllowList) + args.Cancel(); + } + + private void OnDeviceShutdown(EntityUid uid, DeviceListComponent component, ref DeviceShutDownEvent args) + { + component.Devices.Remove(args.ShutDownEntityUid); + Dirty(component); + } + private void OnMapSave(BeforeSaveEvent ev) { List toRemove = new(); @@ -64,69 +153,4 @@ public sealed class DeviceListSystem : SharedDeviceListSystem toRemove.Clear(); } } - - public void OnInit(EntityUid uid, DeviceListComponent component, ComponentInit args) - { - Dirty(component); - } - - /// - /// Gets the given device list as a dictionary - /// - /// - /// If any entity in the device list is pre-map init, it will show the entity UID of the device instead. - /// - public Dictionary GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null) - { - if (!Resolve(uid, ref deviceList)) - return new Dictionary(); - - var devices = new Dictionary(deviceList.Devices.Count); - - foreach (var deviceUid in deviceList.Devices) - { - if (!TryComp(deviceUid, out DeviceNetworkComponent? deviceNet)) - continue; - - var address = MetaData(deviceUid).EntityLifeStage == EntityLifeStage.MapInitialized - ? deviceNet.Address - : $"UID: {deviceUid.ToString()}"; - - devices.Add(address, deviceUid); - - } - - return devices; - } - - /// - /// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field - /// - private void OnBeforeBroadcast(EntityUid uid, DeviceListComponent component, BeforeBroadcastAttemptEvent args) - { - //Don't filter anything if the device list is empty - if (component.Devices.Count == 0) - { - if (component.IsAllowList) args.Cancel(); - return; - } - - HashSet filteredRecipients = new(args.Recipients.Count); - - foreach (var recipient in args.Recipients) - { - if (component.Devices.Contains(recipient.Owner) == component.IsAllowList) filteredRecipients.Add(recipient); - } - - args.ModifiedRecipients = filteredRecipients; - } - - /// - /// Filters incoming packets if that is enabled - /// - private void OnBeforePacketSent(EntityUid uid, DeviceListComponent component, BeforePacketSentEvent args) - { - if (component.HandleIncomingPackets && component.Devices.Contains(args.Sender) != component.IsAllowList) - args.Cancel(); - } } diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs index 5a76c5ed66..c03bbcd229 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -111,6 +111,17 @@ namespace Content.Server.DeviceNetwork.Systems /// private void OnNetworkShutdown(EntityUid uid, DeviceNetworkComponent component, ComponentShutdown args) { + var eventArgs = new DeviceShutDownEvent(uid); + + foreach (var shutdownSubscriberId in component.ShutdownSubscribers) + { + RaiseLocalEvent(shutdownSubscriberId, ref eventArgs); + + DeviceNetworkComponent? device = null!; + if (Resolve(shutdownSubscriberId, ref device)) + device.ShutdownSubscribers.Remove(uid); + } + GetNetwork(component.DeviceNetId).Remove(component); } @@ -224,6 +235,36 @@ namespace Content.Server.DeviceNetwork.Systems deviceNet.Add(device); } + public void SubscribeToDeviceShutdown( + EntityUid subscriberId, EntityUid targetId, + DeviceNetworkComponent? subscribingDevice = null, + DeviceNetworkComponent? targetDevice = null) + { + if (subscriberId == targetId) + return; + + if (!Resolve(subscriberId, ref subscribingDevice) || !Resolve(targetId, ref targetDevice)) + return; + + targetDevice.ShutdownSubscribers.Add(subscriberId); + subscribingDevice.ShutdownSubscribers.Add(targetId); + } + + public void UnsubscribeFromDeviceShutdown( + EntityUid subscriberId, EntityUid targetId, + DeviceNetworkComponent? subscribingDevice = null, + DeviceNetworkComponent? targetDevice = null) + { + if (subscriberId == targetId) + return; + + if (!Resolve(subscriberId, ref subscribingDevice) || !Resolve(targetId, ref targetDevice)) + return; + + targetDevice.ShutdownSubscribers.Remove(subscriberId); + subscribingDevice.ShutdownSubscribers.Remove(targetId); + } + /// /// Try to find a device on a network using its address. /// @@ -408,4 +449,11 @@ namespace Content.Server.DeviceNetwork.Systems Data = data; } } + + /// + /// Gets raised on entities that subscribed to shutdown event of the shut down entity + /// + /// The entity that was shut down + [ByRefEvent] + public readonly record struct DeviceShutDownEvent(EntityUid ShutDownEntityUid); } diff --git a/Content.Shared/DeviceNetwork/Components/DeviceListComponent.cs b/Content.Shared/DeviceNetwork/Components/DeviceListComponent.cs index a4b7ea9250..e52ac766ba 100644 --- a/Content.Shared/DeviceNetwork/Components/DeviceListComponent.cs +++ b/Content.Shared/DeviceNetwork/Components/DeviceListComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.GameStates; +using Content.Shared.DeviceNetwork.Systems; +using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared.DeviceNetwork.Components; diff --git a/Content.Shared/DeviceNetwork/Systems/SharedDeviceListSystem.cs b/Content.Shared/DeviceNetwork/Systems/SharedDeviceListSystem.cs index 88de21b493..2f9112d58e 100644 --- a/Content.Shared/DeviceNetwork/Systems/SharedDeviceListSystem.cs +++ b/Content.Shared/DeviceNetwork/Systems/SharedDeviceListSystem.cs @@ -2,7 +2,7 @@ using System.Linq; using Content.Shared.DeviceNetwork.Components; using Robust.Shared.GameStates; -namespace Content.Shared.DeviceNetwork; +namespace Content.Shared.DeviceNetwork.Systems; public abstract class SharedDeviceListSystem : EntitySystem { @@ -36,6 +36,8 @@ public abstract class SharedDeviceListSystem : EntitySystem deviceList.Devices = newDevices; + UpdateShutdownSubscription(uid, devicesList, oldDevices); + RaiseLocalEvent(uid, new DeviceListUpdateEvent(oldDevices, devicesList)); Dirty(deviceList); @@ -52,6 +54,10 @@ public abstract class SharedDeviceListSystem : EntitySystem return component.Devices; } + protected virtual void UpdateShutdownSubscription(EntityUid uid, List devicesList, List oldDevices) + { + } + private void GetDeviceListState(EntityUid uid, DeviceListComponent comp, ref ComponentGetState args) { args.State = new DeviceListComponentState(comp.Devices, comp.IsAllowList, comp.HandleIncomingPackets);