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
This commit is contained in:
Julian Giebel
2023-05-26 00:08:22 +02:00
committed by GitHub
parent a02753545d
commit 2bb1fde4dc
8 changed files with 157 additions and 67 deletions

View File

@@ -1,4 +1,5 @@
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Systems;
namespace Content.Client.NetworkConfigurator.Systems; namespace Content.Client.NetworkConfigurator.Systems;

View File

@@ -14,6 +14,7 @@ using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Wires; using Content.Shared.Wires;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;

View File

@@ -9,6 +9,7 @@ using Content.Shared.AlertLevel;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;

View File

@@ -101,5 +101,13 @@ namespace Content.Server.DeviceNetwork.Components
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
[DataField("sendBroadcastAttemptEvent")] [DataField("sendBroadcastAttemptEvent")]
public bool SendBroadcastAttemptEvent = false; public bool SendBroadcastAttemptEvent = false;
/// <summary>
/// A list of entities that get sent the <see cref="DeviceShutDownEvent"/> when this entity gets deleted.<br/><br/>
/// 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.
/// </summary>
[DataField("ShutdownSubscribers")]
public HashSet<EntityUid> ShutdownSubscribers = new();
} }
} }

View File

@@ -2,6 +2,7 @@
using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components; using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Map.Events; using Robust.Shared.Map.Events;
@@ -13,16 +14,104 @@ public sealed class DeviceListSystem : SharedDeviceListSystem
{ {
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
[Dependency] private DeviceNetworkSystem _deviceNetworkSystem = null!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DeviceListComponent, ComponentInit>(OnInit); SubscribeLocalEvent<DeviceListComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<DeviceListComponent, BeforeBroadcastAttemptEvent>(OnBeforeBroadcast); SubscribeLocalEvent<DeviceListComponent, BeforeBroadcastAttemptEvent>(OnBeforeBroadcast);
SubscribeLocalEvent<DeviceListComponent, BeforePacketSentEvent>(OnBeforePacketSent); SubscribeLocalEvent<DeviceListComponent, BeforePacketSentEvent>(OnBeforePacketSent);
SubscribeLocalEvent<DeviceListComponent, DeviceShutDownEvent>(OnDeviceShutdown);
SubscribeLocalEvent<BeforeSaveEvent>(OnMapSave); SubscribeLocalEvent<BeforeSaveEvent>(OnMapSave);
_sawmill = Logger.GetSawmill("devicelist"); _sawmill = Logger.GetSawmill("devicelist");
} }
public void OnInit(EntityUid uid, DeviceListComponent component, ComponentInit args)
{
Dirty(component);
}
/// <summary>
/// Gets the given device list as a dictionary
/// </summary>
/// <remarks>
/// If any entity in the device list is pre-map init, it will show the entity UID of the device instead.
/// </remarks>
public Dictionary<string, EntityUid> GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null)
{
if (!Resolve(uid, ref deviceList))
return new Dictionary<string, EntityUid>();
var devices = new Dictionary<string, EntityUid>(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<EntityUid> newDevices, List<EntityUid> oldDevices)
{
foreach (var device in newDevices)
{
_deviceNetworkSystem.SubscribeToDeviceShutdown(uid, device);
}
var removedDevices = oldDevices.Except(newDevices);
foreach (var device in removedDevices)
{
_deviceNetworkSystem.UnsubscribeFromDeviceShutdown(uid, device);
}
}
/// <summary>
/// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field
/// </summary>
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<DeviceNetworkComponent> 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;
}
/// <summary>
/// Filters incoming packets if that is enabled <see cref="OnBeforeBroadcast"/>
/// </summary>
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) private void OnMapSave(BeforeSaveEvent ev)
{ {
List<EntityUid> toRemove = new(); List<EntityUid> toRemove = new();
@@ -64,69 +153,4 @@ public sealed class DeviceListSystem : SharedDeviceListSystem
toRemove.Clear(); toRemove.Clear();
} }
} }
public void OnInit(EntityUid uid, DeviceListComponent component, ComponentInit args)
{
Dirty(component);
}
/// <summary>
/// Gets the given device list as a dictionary
/// </summary>
/// <remarks>
/// If any entity in the device list is pre-map init, it will show the entity UID of the device instead.
/// </remarks>
public Dictionary<string, EntityUid> GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null)
{
if (!Resolve(uid, ref deviceList))
return new Dictionary<string, EntityUid>();
var devices = new Dictionary<string, EntityUid>(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;
}
/// <summary>
/// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field
/// </summary>
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<DeviceNetworkComponent> 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;
}
/// <summary>
/// Filters incoming packets if that is enabled <see cref="OnBeforeBroadcast"/>
/// </summary>
private void OnBeforePacketSent(EntityUid uid, DeviceListComponent component, BeforePacketSentEvent args)
{
if (component.HandleIncomingPackets && component.Devices.Contains(args.Sender) != component.IsAllowList)
args.Cancel();
}
} }

View File

@@ -111,6 +111,17 @@ namespace Content.Server.DeviceNetwork.Systems
/// </summary> /// </summary>
private void OnNetworkShutdown(EntityUid uid, DeviceNetworkComponent component, ComponentShutdown args) 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); GetNetwork(component.DeviceNetId).Remove(component);
} }
@@ -224,6 +235,36 @@ namespace Content.Server.DeviceNetwork.Systems
deviceNet.Add(device); 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);
}
/// <summary> /// <summary>
/// Try to find a device on a network using its address. /// Try to find a device on a network using its address.
/// </summary> /// </summary>
@@ -408,4 +449,11 @@ namespace Content.Server.DeviceNetwork.Systems
Data = data; Data = data;
} }
} }
/// <summary>
/// Gets raised on entities that subscribed to shutdown event of the shut down entity
/// </summary>
/// <param name="ShutDownEntityUid">The entity that was shut down</param>
[ByRefEvent]
public readonly record struct DeviceShutDownEvent(EntityUid ShutDownEntityUid);
} }

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameStates; using Content.Shared.DeviceNetwork.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork.Components; namespace Content.Shared.DeviceNetwork.Components;

View File

@@ -2,7 +2,7 @@ using System.Linq;
using Content.Shared.DeviceNetwork.Components; using Content.Shared.DeviceNetwork.Components;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork.Systems;
public abstract class SharedDeviceListSystem : EntitySystem public abstract class SharedDeviceListSystem : EntitySystem
{ {
@@ -36,6 +36,8 @@ public abstract class SharedDeviceListSystem : EntitySystem
deviceList.Devices = newDevices; deviceList.Devices = newDevices;
UpdateShutdownSubscription(uid, devicesList, oldDevices);
RaiseLocalEvent(uid, new DeviceListUpdateEvent(oldDevices, devicesList)); RaiseLocalEvent(uid, new DeviceListUpdateEvent(oldDevices, devicesList));
Dirty(deviceList); Dirty(deviceList);
@@ -52,6 +54,10 @@ public abstract class SharedDeviceListSystem : EntitySystem
return component.Devices; return component.Devices;
} }
protected virtual void UpdateShutdownSubscription(EntityUid uid, List<EntityUid> devicesList, List<EntityUid> oldDevices)
{
}
private void GetDeviceListState(EntityUid uid, DeviceListComponent comp, ref ComponentGetState args) private void GetDeviceListState(EntityUid uid, DeviceListComponent comp, ref ComponentGetState args)
{ {
args.State = new DeviceListComponentState(comp.Devices, comp.IsAllowList, comp.HandleIncomingPackets); args.State = new DeviceListComponentState(comp.Devices, comp.IsAllowList, comp.HandleIncomingPackets);