DeviceLinkSystem.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. using Content.Server.DeviceLinking.Components;
  2. using Content.Server.DeviceLinking.Events;
  3. using Content.Server.DeviceNetwork;
  4. using Content.Server.DeviceNetwork.Components;
  5. using Content.Server.DeviceNetwork.Systems;
  6. using Content.Shared.DeviceLinking;
  7. using Content.Shared.DeviceLinking.Events;
  8. using Content.Shared.DeviceNetwork;
  9. namespace Content.Server.DeviceLinking.Systems;
  10. public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
  11. {
  12. [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
  13. public override void Initialize()
  14. {
  15. base.Initialize();
  16. SubscribeLocalEvent<DeviceLinkSinkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
  17. SubscribeLocalEvent<DeviceLinkSourceComponent, NewLinkEvent>(OnNewLink);
  18. }
  19. public override void Update(float frameTime)
  20. {
  21. var query = EntityQueryEnumerator<DeviceLinkSinkComponent>();
  22. while (query.MoveNext(out var component))
  23. {
  24. if (component.InvokeLimit < 1)
  25. {
  26. component.InvokeCounter = 0;
  27. continue;
  28. }
  29. if(component.InvokeCounter > 0)
  30. component.InvokeCounter--;
  31. }
  32. }
  33. #region Sending & Receiving
  34. public override void InvokePort(EntityUid uid, string port, NetworkPayload? data = null, DeviceLinkSourceComponent? sourceComponent = null)
  35. {
  36. if (!Resolve(uid, ref sourceComponent) || !sourceComponent.Outputs.TryGetValue(port, out var sinks))
  37. return;
  38. foreach (var sinkUid in sinks)
  39. {
  40. if (!sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var links))
  41. continue;
  42. if (!TryComp<DeviceLinkSinkComponent>(sinkUid, out var sinkComponent))
  43. continue;
  44. foreach (var (source, sink) in links)
  45. {
  46. if (source == port)
  47. InvokeDirect((uid, sourceComponent), (sinkUid, sinkComponent), source, sink, data);
  48. }
  49. }
  50. }
  51. /// <summary>
  52. /// Raises an event on or sends a network packet directly to a sink from a source.
  53. /// </summary>
  54. private void InvokeDirect(Entity<DeviceLinkSourceComponent> source, Entity<DeviceLinkSinkComponent?> sink, string sourcePort, string sinkPort, NetworkPayload? data)
  55. {
  56. if (!Resolve(sink, ref sink.Comp))
  57. return;
  58. if (sink.Comp.InvokeCounter > sink.Comp.InvokeLimit)
  59. {
  60. sink.Comp.InvokeCounter = 0;
  61. var args = new DeviceLinkOverloadedEvent();
  62. RaiseLocalEvent(sink, ref args);
  63. RemoveAllFromSink(sink, sink.Comp);
  64. return;
  65. }
  66. sink.Comp.InvokeCounter++;
  67. //Just skip using device networking if the source or the sink doesn't support it
  68. if (!HasComp<DeviceNetworkComponent>(source) || !TryComp<DeviceNetworkComponent>(sink, out var sinkNetwork))
  69. {
  70. var eventArgs = new SignalReceivedEvent(sinkPort, source);
  71. RaiseLocalEvent(sink, ref eventArgs);
  72. return;
  73. }
  74. var payload = new NetworkPayload()
  75. {
  76. [InvokedPort] = sinkPort
  77. };
  78. if (data != null)
  79. {
  80. //Prevent overriding the invoked port
  81. data.Remove(InvokedPort);
  82. foreach (var (key, value) in data)
  83. {
  84. payload.Add(key, value);
  85. }
  86. }
  87. // force using wireless network so things like atmos devices are able to send signals
  88. var network = (int) DeviceNetworkComponent.DeviceNetIdDefaults.Wireless;
  89. _deviceNetworkSystem.QueuePacket(source, sinkNetwork.Address, payload, sinkNetwork.ReceiveFrequency, network);
  90. }
  91. /// <summary>
  92. /// Helper function that invokes a port with a high/low binary logic signal.
  93. /// </summary>
  94. public void SendSignal(EntityUid uid, string port, bool signal, DeviceLinkSourceComponent? comp = null)
  95. {
  96. if (!Resolve(uid, ref comp))
  97. return;
  98. var data = new NetworkPayload
  99. {
  100. [DeviceNetworkConstants.LogicState] = signal ? SignalState.High : SignalState.Low
  101. };
  102. InvokePort(uid, port, data, comp);
  103. comp.LastSignals[port] = signal;
  104. }
  105. /// <summary>
  106. /// Clears the last signals state for linking.
  107. /// This is not to be confused with sending a low signal, this is the complete absence of anything.
  108. /// Use if the device is in an invalid state and has no reasonable output signal.
  109. /// </summary>
  110. public void ClearSignal(Entity<DeviceLinkSourceComponent?> ent, string port)
  111. {
  112. if (!Resolve(ent, ref ent.Comp))
  113. return;
  114. ent.Comp.LastSignals.Remove(port);
  115. }
  116. /// <summary>
  117. /// Checks if the payload has a port defined and if the port is present on the sink.
  118. /// Raises a <see cref="SignalReceivedEvent"/> containing the payload when the check passes
  119. /// </summary>
  120. private void OnPacketReceived(EntityUid uid, DeviceLinkSinkComponent component, DeviceNetworkPacketEvent args)
  121. {
  122. if (!args.Data.TryGetValue(InvokedPort, out string? port) || !(component.Ports?.Contains(port) ?? false))
  123. return;
  124. var eventArgs = new SignalReceivedEvent(port, args.Sender, args.Data);
  125. RaiseLocalEvent(uid, ref eventArgs);
  126. }
  127. /// <summary>
  128. /// When linking from a port that currently has a signal being sent, invoke the new link with that signal.
  129. /// </summary>
  130. private void OnNewLink(Entity<DeviceLinkSourceComponent> ent, ref NewLinkEvent args)
  131. {
  132. if (args.Source != ent.Owner)
  133. return;
  134. // only do anything if a signal is being sent from a port
  135. if (!ent.Comp.LastSignals.TryGetValue(args.SourcePort, out var signal))
  136. return;
  137. var payload = new NetworkPayload()
  138. {
  139. [DeviceNetworkConstants.LogicState] = signal ? SignalState.High : SignalState.Low
  140. };
  141. InvokeDirect(ent, args.Sink, args.SourcePort, args.SinkPort, payload);
  142. }
  143. #endregion
  144. }