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);