DeviceListSystem.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using System.Linq;
  2. using Content.Server.DeviceNetwork.Components;
  3. using Content.Shared.DeviceNetwork;
  4. using Content.Shared.DeviceNetwork.Components;
  5. using Content.Shared.DeviceNetwork.Systems;
  6. using Content.Shared.Interaction;
  7. using JetBrains.Annotations;
  8. using Robust.Shared.Map.Events;
  9. namespace Content.Server.DeviceNetwork.Systems;
  10. [UsedImplicitly]
  11. public sealed class DeviceListSystem : SharedDeviceListSystem
  12. {
  13. [Dependency] private readonly NetworkConfiguratorSystem _configurator = default!;
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeLocalEvent<DeviceListComponent, ComponentShutdown>(OnShutdown);
  18. SubscribeLocalEvent<DeviceListComponent, BeforeBroadcastAttemptEvent>(OnBeforeBroadcast);
  19. SubscribeLocalEvent<DeviceListComponent, BeforePacketSentEvent>(OnBeforePacketSent);
  20. SubscribeLocalEvent<BeforeSerializationEvent>(OnMapSave);
  21. }
  22. private void OnShutdown(EntityUid uid, DeviceListComponent component, ComponentShutdown args)
  23. {
  24. foreach (var conf in component.Configurators)
  25. {
  26. _configurator.OnDeviceListShutdown(conf, (uid, component));
  27. }
  28. var query = GetEntityQuery<DeviceNetworkComponent>();
  29. foreach (var device in component.Devices)
  30. {
  31. if (query.TryGetComponent(device, out var comp))
  32. comp.DeviceLists.Remove(uid);
  33. }
  34. component.Devices.Clear();
  35. }
  36. /// <summary>
  37. /// Gets the given device list as a dictionary
  38. /// </summary>
  39. /// <remarks>
  40. /// If any entity in the device list is pre-map init, it will show the entity UID of the device instead.
  41. /// </remarks>
  42. public Dictionary<string, EntityUid> GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null)
  43. {
  44. if (!Resolve(uid, ref deviceList))
  45. return new Dictionary<string, EntityUid>();
  46. var devices = new Dictionary<string, EntityUid>(deviceList.Devices.Count);
  47. foreach (var deviceUid in deviceList.Devices)
  48. {
  49. if (!TryComp(deviceUid, out DeviceNetworkComponent? deviceNet))
  50. continue;
  51. var address = MetaData(deviceUid).EntityLifeStage == EntityLifeStage.MapInitialized
  52. ? deviceNet.Address
  53. : $"UID: {deviceUid.ToString()}";
  54. devices.Add(address, deviceUid);
  55. }
  56. return devices;
  57. }
  58. /// <summary>
  59. /// Checks if the given address is present in a device list
  60. /// </summary>
  61. /// <param name="uid">The entity uid that has the device list that should be checked for the address</param>
  62. /// <param name="address">The address to check for</param>
  63. /// <param name="deviceList">The device list component</param>
  64. /// <returns>True if the address is present. False if not</returns>
  65. public bool ExistsInDeviceList(EntityUid uid, string address, DeviceListComponent? deviceList = null)
  66. {
  67. var addresses = GetDeviceList(uid).Keys;
  68. return addresses.Contains(address);
  69. }
  70. /// <summary>
  71. /// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field
  72. /// </summary>
  73. private void OnBeforeBroadcast(EntityUid uid, DeviceListComponent component, BeforeBroadcastAttemptEvent args)
  74. {
  75. //Don't filter anything if the device list is empty
  76. if (component.Devices.Count == 0)
  77. {
  78. if (component.IsAllowList)
  79. args.Cancel();
  80. return;
  81. }
  82. HashSet<DeviceNetworkComponent> filteredRecipients = new(args.Recipients.Count);
  83. foreach (var recipient in args.Recipients)
  84. {
  85. if (component.Devices.Contains(recipient.Owner) == component.IsAllowList)
  86. filteredRecipients.Add(recipient);
  87. }
  88. args.ModifiedRecipients = filteredRecipients;
  89. }
  90. /// <summary>
  91. /// Filters incoming packets if that is enabled <see cref="OnBeforeBroadcast"/>
  92. /// </summary>
  93. private void OnBeforePacketSent(EntityUid uid, DeviceListComponent component, BeforePacketSentEvent args)
  94. {
  95. if (component.HandleIncomingPackets && component.Devices.Contains(args.Sender) != component.IsAllowList)
  96. args.Cancel();
  97. }
  98. public void OnDeviceShutdown(Entity<DeviceListComponent?> list, Entity<DeviceNetworkComponent> device)
  99. {
  100. device.Comp.DeviceLists.Remove(list.Owner);
  101. if (!Resolve(list.Owner, ref list.Comp))
  102. return;
  103. list.Comp.Devices.Remove(device);
  104. Dirty(list);
  105. }
  106. private void OnMapSave(BeforeSerializationEvent ev)
  107. {
  108. List<EntityUid> toRemove = new();
  109. var query = GetEntityQuery<TransformComponent>();
  110. var enumerator = AllEntityQuery<DeviceListComponent, TransformComponent>();
  111. while (enumerator.MoveNext(out var uid, out var device, out var xform))
  112. {
  113. if (!ev.MapIds.Contains(xform.MapID))
  114. continue;
  115. foreach (var ent in device.Devices)
  116. {
  117. if (!query.TryGetComponent(ent, out var linkedXform))
  118. {
  119. // Entity was deleted.
  120. // TODO remove these on deletion instead of on-save.
  121. toRemove.Add(ent);
  122. continue;
  123. }
  124. // This is assuming that **all** of the map is getting saved.
  125. // Which is not necessarily true.
  126. // AAAAAAAAAAAAAA
  127. if (ev.MapIds.Contains(linkedXform.MapID))
  128. continue;
  129. toRemove.Add(ent);
  130. // TODO full game saves.
  131. // when full saves are supported, this should instead add data to the BeforeSaveEvent informing the
  132. // saving system that this map (or null-space entity) also needs to be included in the save.
  133. Log.Error(
  134. $"Saving a device list ({ToPrettyString(uid)}) that has a reference to an entity on another map ({ToPrettyString(ent)}). Removing entity from list.");
  135. }
  136. if (toRemove.Count == 0)
  137. continue;
  138. var old = device.Devices.ToList();
  139. device.Devices.ExceptWith(toRemove);
  140. RaiseLocalEvent(uid, new DeviceListUpdateEvent(old, device.Devices.ToList()));
  141. Dirty(uid, device);
  142. toRemove.Clear();
  143. }
  144. }
  145. /// <summary>
  146. /// Updates the device list stored on this entity.
  147. /// </summary>
  148. /// <param name="uid">The entity to update.</param>
  149. /// <param name="devices">The devices to store.</param>
  150. /// <param name="merge">Whether to merge or replace the devices stored.</param>
  151. /// <param name="deviceList">Device list component</param>
  152. public DeviceListUpdateResult UpdateDeviceList(EntityUid uid, IEnumerable<EntityUid> devices, bool merge = false, DeviceListComponent? deviceList = null)
  153. {
  154. if (!Resolve(uid, ref deviceList))
  155. return DeviceListUpdateResult.NoComponent;
  156. var list = devices.ToList();
  157. var newDevices = new HashSet<EntityUid>(list);
  158. if (merge)
  159. newDevices.UnionWith(deviceList.Devices);
  160. if (newDevices.Count > deviceList.DeviceLimit)
  161. {
  162. return DeviceListUpdateResult.TooManyDevices;
  163. }
  164. var query = GetEntityQuery<DeviceNetworkComponent>();
  165. var oldDevices = deviceList.Devices.ToList();
  166. foreach (var device in oldDevices)
  167. {
  168. if (newDevices.Contains(device))
  169. continue;
  170. deviceList.Devices.Remove(device);
  171. if (query.TryGetComponent(device, out var comp))
  172. comp.DeviceLists.Remove(uid);
  173. }
  174. foreach (var device in newDevices)
  175. {
  176. if (!query.TryGetComponent(device, out var comp))
  177. continue;
  178. if (!deviceList.Devices.Add(device))
  179. continue;
  180. comp.DeviceLists.Add(uid);
  181. }
  182. RaiseLocalEvent(uid, new DeviceListUpdateEvent(oldDevices, list));
  183. Dirty(uid, deviceList);
  184. return DeviceListUpdateResult.UpdateOk;
  185. }
  186. }