1
0

EncryptionKeySystem.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using System.Linq;
  2. using Content.Shared.Chat;
  3. using Content.Shared.DoAfter;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Hands.EntitySystems;
  6. using Content.Shared.Interaction;
  7. using Content.Shared.Popups;
  8. using Content.Shared.Radio.Components;
  9. using Content.Shared.Tools.Components;
  10. using Content.Shared.Wires;
  11. using Robust.Shared.Audio.Systems;
  12. using Robust.Shared.Containers;
  13. using Robust.Shared.Network;
  14. using Robust.Shared.Prototypes;
  15. using Robust.Shared.Serialization;
  16. using Robust.Shared.Timing;
  17. using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
  18. namespace Content.Shared.Radio.EntitySystems;
  19. /// <summary>
  20. /// This system manages encryption keys & key holders for use with radio channels.
  21. /// </summary>
  22. public sealed partial class EncryptionKeySystem : EntitySystem
  23. {
  24. [Dependency] private readonly IPrototypeManager _protoManager = default!;
  25. [Dependency] private readonly IGameTiming _timing = default!;
  26. [Dependency] private readonly INetManager _net = default!;
  27. [Dependency] private readonly SharedToolSystem _tool = default!;
  28. [Dependency] private readonly SharedPopupSystem _popup = default!;
  29. [Dependency] private readonly SharedContainerSystem _container = default!;
  30. [Dependency] private readonly SharedAudioSystem _audio = default!;
  31. [Dependency] private readonly SharedHandsSystem _hands = default!;
  32. [Dependency] private readonly SharedWiresSystem _wires = default!;
  33. public override void Initialize()
  34. {
  35. base.Initialize();
  36. SubscribeLocalEvent<EncryptionKeyComponent, ExaminedEvent>(OnKeyExamined);
  37. SubscribeLocalEvent<EncryptionKeyHolderComponent, ExaminedEvent>(OnHolderExamined);
  38. SubscribeLocalEvent<EncryptionKeyHolderComponent, ComponentStartup>(OnStartup);
  39. SubscribeLocalEvent<EncryptionKeyHolderComponent, InteractUsingEvent>(OnInteractUsing);
  40. SubscribeLocalEvent<EncryptionKeyHolderComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
  41. SubscribeLocalEvent<EncryptionKeyHolderComponent, EntRemovedFromContainerMessage>(OnContainerModified);
  42. SubscribeLocalEvent<EncryptionKeyHolderComponent, EncryptionRemovalFinishedEvent>(OnKeyRemoval);
  43. }
  44. private void OnKeyRemoval(EntityUid uid, EncryptionKeyHolderComponent component, EncryptionRemovalFinishedEvent args)
  45. {
  46. if (args.Cancelled)
  47. return;
  48. var contained = component.KeyContainer.ContainedEntities.ToArray();
  49. _container.EmptyContainer(component.KeyContainer, reparent: false);
  50. foreach (var ent in contained)
  51. {
  52. _hands.PickupOrDrop(args.User, ent, dropNear: true);
  53. }
  54. if (!_timing.IsFirstTimePredicted)
  55. return;
  56. // TODO add predicted pop-up overrides.
  57. if (_net.IsServer)
  58. _popup.PopupEntity(Loc.GetString("encryption-keys-all-extracted"), uid, args.User);
  59. _audio.PlayPredicted(component.KeyExtractionSound, uid, args.User);
  60. }
  61. public void UpdateChannels(EntityUid uid, EncryptionKeyHolderComponent component)
  62. {
  63. if (!component.Initialized)
  64. return;
  65. component.Channels.Clear();
  66. component.DefaultChannel = null;
  67. foreach (var ent in component.KeyContainer.ContainedEntities)
  68. {
  69. if (TryComp<EncryptionKeyComponent>(ent, out var key))
  70. {
  71. component.Channels.UnionWith(key.Channels);
  72. component.DefaultChannel ??= key.DefaultChannel;
  73. }
  74. }
  75. RaiseLocalEvent(uid, new EncryptionChannelsChangedEvent(component));
  76. }
  77. private void OnContainerModified(EntityUid uid, EncryptionKeyHolderComponent component, ContainerModifiedMessage args)
  78. {
  79. if (args.Container.ID == EncryptionKeyHolderComponent.KeyContainerName)
  80. UpdateChannels(uid, component);
  81. }
  82. private void OnInteractUsing(EntityUid uid, EncryptionKeyHolderComponent component, InteractUsingEvent args)
  83. {
  84. if (args.Handled)
  85. return;
  86. if (HasComp<EncryptionKeyComponent>(args.Used))
  87. {
  88. args.Handled = true;
  89. TryInsertKey(uid, component, args);
  90. }
  91. else if (TryComp<ToolComponent>(args.Used, out var tool)
  92. && _tool.HasQuality(args.Used, component.KeysExtractionMethod, tool)
  93. && component.KeyContainer.ContainedEntities.Count > 0) // dont block deconstruction
  94. {
  95. args.Handled = true;
  96. TryRemoveKey(uid, component, args, tool);
  97. }
  98. }
  99. private void TryInsertKey(EntityUid uid, EncryptionKeyHolderComponent component, InteractUsingEvent args)
  100. {
  101. if (!component.KeysUnlocked)
  102. {
  103. _popup.PopupClient(Loc.GetString("encryption-keys-are-locked"), uid, args.User);
  104. return;
  105. }
  106. if (TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open)
  107. {
  108. _popup.PopupClient(Loc.GetString("encryption-keys-panel-locked"), uid, args.User);
  109. return;
  110. }
  111. if (component.KeySlots <= component.KeyContainer.ContainedEntities.Count)
  112. {
  113. _popup.PopupClient(Loc.GetString("encryption-key-slots-already-full"), uid, args.User);
  114. return;
  115. }
  116. if (_container.Insert(args.Used, component.KeyContainer))
  117. {
  118. _popup.PopupClient(Loc.GetString("encryption-key-successfully-installed"), uid, args.User);
  119. _audio.PlayPredicted(component.KeyInsertionSound, args.Target, args.User);
  120. args.Handled = true;
  121. return;
  122. }
  123. }
  124. private void TryRemoveKey(EntityUid uid, EncryptionKeyHolderComponent component, InteractUsingEvent args,
  125. ToolComponent? tool)
  126. {
  127. if (!component.KeysUnlocked)
  128. {
  129. _popup.PopupClient(Loc.GetString("encryption-keys-are-locked"), uid, args.User);
  130. return;
  131. }
  132. if (!_wires.IsPanelOpen(uid))
  133. {
  134. _popup.PopupClient(Loc.GetString("encryption-keys-panel-locked"), uid, args.User);
  135. return;
  136. }
  137. if (component.KeyContainer.ContainedEntities.Count == 0)
  138. {
  139. _popup.PopupClient(Loc.GetString("encryption-keys-no-keys"), uid, args.User);
  140. return;
  141. }
  142. _tool.UseTool(args.Used, args.User, uid, 1f, component.KeysExtractionMethod, new EncryptionRemovalFinishedEvent(), toolComponent: tool);
  143. }
  144. private void OnStartup(EntityUid uid, EncryptionKeyHolderComponent component, ComponentStartup args)
  145. {
  146. component.KeyContainer = _container.EnsureContainer<Container>(uid, EncryptionKeyHolderComponent.KeyContainerName);
  147. UpdateChannels(uid, component);
  148. }
  149. private void OnHolderExamined(EntityUid uid, EncryptionKeyHolderComponent component, ExaminedEvent args)
  150. {
  151. if (!args.IsInDetailsRange)
  152. return;
  153. if (component.KeyContainer.ContainedEntities.Count == 0)
  154. {
  155. args.PushMarkup(Loc.GetString("encryption-keys-no-keys"));
  156. return;
  157. }
  158. if (component.Channels.Count > 0)
  159. {
  160. using (args.PushGroup(nameof(EncryptionKeyComponent)))
  161. {
  162. args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix"));
  163. AddChannelsExamine(component.Channels,
  164. component.DefaultChannel,
  165. args,
  166. _protoManager,
  167. "examine-encryption-channel");
  168. }
  169. }
  170. }
  171. private void OnKeyExamined(EntityUid uid, EncryptionKeyComponent component, ExaminedEvent args)
  172. {
  173. if (!args.IsInDetailsRange)
  174. return;
  175. if(component.Channels.Count > 0)
  176. {
  177. args.PushMarkup(Loc.GetString("examine-encryption-channels-prefix"));
  178. AddChannelsExamine(component.Channels, component.DefaultChannel, args, _protoManager, "examine-encryption-channel");
  179. }
  180. }
  181. /// <summary>
  182. /// A method for formating list of radio channels for examine events.
  183. /// </summary>
  184. /// <param name="channels">HashSet of channels in headset, encryptionkey or etc.</param>
  185. /// <param name="protoManager">IPrototypeManager for getting prototypes of channels with their variables.</param>
  186. /// <param name="channelFTLPattern">String that provide id of pattern in .ftl files to format channel with variables of it.</param>
  187. public void AddChannelsExamine(HashSet<string> channels, string? defaultChannel, ExaminedEvent examineEvent, IPrototypeManager protoManager, string channelFTLPattern)
  188. {
  189. RadioChannelPrototype? proto;
  190. foreach (var id in channels)
  191. {
  192. proto = _protoManager.Index<RadioChannelPrototype>(id);
  193. var key = id == SharedChatSystem.CommonChannel
  194. ? SharedChatSystem.RadioCommonPrefix.ToString()
  195. : $"{SharedChatSystem.RadioChannelPrefix}{proto.KeyCode}";
  196. examineEvent.PushMarkup(Loc.GetString(channelFTLPattern,
  197. ("color", proto.Color),
  198. ("key", key),
  199. ("id", proto.LocalizedName),
  200. ("freq", proto.Frequency / 10f)));
  201. }
  202. if (defaultChannel != null && _protoManager.TryIndex(defaultChannel, out proto))
  203. {
  204. if (HasComp<HeadsetComponent>(examineEvent.Examined))
  205. {
  206. var msg = Loc.GetString("examine-headset-default-channel",
  207. ("prefix", SharedChatSystem.DefaultChannelPrefix),
  208. ("channel", proto.LocalizedName),
  209. ("color", proto.Color));
  210. examineEvent.PushMarkup(msg);
  211. }
  212. if (HasComp<EncryptionKeyComponent>(examineEvent.Examined))
  213. {
  214. var msg = Loc.GetString("examine-encryption-default-channel",
  215. ("channel", proto.LocalizedName),
  216. ("color", proto.Color));
  217. examineEvent.PushMarkup(msg);
  218. }
  219. }
  220. }
  221. [Serializable, NetSerializable]
  222. public sealed partial class EncryptionRemovalFinishedEvent : SimpleDoAfterEvent
  223. {
  224. }
  225. }