-using Content.Server.DeviceLinking.Components;
-using Content.Server.DeviceLinking.Events;
+using Content.Server.DeviceLinking.Components;
+using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Shared.DeviceLinking;
+using Content.Shared.DeviceLinking.Events;
using Content.Shared.DeviceNetwork;
namespace Content.Server.DeviceLinking.Systems;
public override void Initialize()
{
base.Initialize();
+
SubscribeLocalEvent<DeviceLinkSinkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+ SubscribeLocalEvent<DeviceLinkSourceComponent, NewLinkEvent>(OnNewLink);
}
public override void Update(float frameTime)
foreach (var (source, sink) in links)
{
- if (source != port)
- continue;
-
- if (sinkComponent.InvokeCounter > sinkComponent.InvokeLimit)
- {
- sinkComponent.InvokeCounter = 0;
- var args = new DeviceLinkOverloadedEvent();
- RaiseLocalEvent(sinkUid, ref args);
- RemoveAllFromSink(sinkUid, sinkComponent);
- continue;
- }
-
- sinkComponent.InvokeCounter++;
-
- //Just skip using device networking if the source or the sink doesn't support it
- if (!HasComp<DeviceNetworkComponent>(uid) || !TryComp<DeviceNetworkComponent>(sinkUid, out var sinkNetworkComponent))
- {
- var eventArgs = new SignalReceivedEvent(sink, uid);
-
- RaiseLocalEvent(sinkUid, ref eventArgs);
- continue;
- }
-
- var payload = new NetworkPayload()
- {
- [InvokedPort] = sink
- };
-
- if (data != null)
- {
- //Prevent overriding the invoked port
- data.Remove(InvokedPort);
- foreach (var (key, value) in data)
- {
- payload.Add(key, value);
- }
- }
-
- // force using wireless network so things like atmos devices are able to send signals
- var network = (int) DeviceNetworkComponent.DeviceNetIdDefaults.Wireless;
- _deviceNetworkSystem.QueuePacket(uid, sinkNetworkComponent.Address, payload, sinkNetworkComponent.ReceiveFrequency, network);
+ if (source == port)
+ InvokeDirect((uid, sourceComponent), (sinkUid, sinkComponent), source, sink, data);
}
}
}
+ /// <summary>
+ /// Raises an event on or sends a network packet directly to a sink from a source.
+ /// </summary>
+ private void InvokeDirect(Entity<DeviceLinkSourceComponent> source, Entity<DeviceLinkSinkComponent?> sink, string sourcePort, string sinkPort, NetworkPayload? data)
+ {
+ if (!Resolve(sink, ref sink.Comp))
+ return;
+
+ if (sink.Comp.InvokeCounter > sink.Comp.InvokeLimit)
+ {
+ sink.Comp.InvokeCounter = 0;
+ var args = new DeviceLinkOverloadedEvent();
+ RaiseLocalEvent(sink, ref args);
+ RemoveAllFromSink(sink, sink.Comp);
+ return;
+ }
+
+ sink.Comp.InvokeCounter++;
+
+ //Just skip using device networking if the source or the sink doesn't support it
+ if (!HasComp<DeviceNetworkComponent>(source) || !TryComp<DeviceNetworkComponent>(sink, out var sinkNetwork))
+ {
+ var eventArgs = new SignalReceivedEvent(sinkPort, source);
+ RaiseLocalEvent(sink, ref eventArgs);
+ return;
+ }
+
+ var payload = new NetworkPayload()
+ {
+ [InvokedPort] = sinkPort
+ };
+
+ if (data != null)
+ {
+ //Prevent overriding the invoked port
+ data.Remove(InvokedPort);
+ foreach (var (key, value) in data)
+ {
+ payload.Add(key, value);
+ }
+ }
+
+ // force using wireless network so things like atmos devices are able to send signals
+ var network = (int) DeviceNetworkComponent.DeviceNetIdDefaults.Wireless;
+ _deviceNetworkSystem.QueuePacket(source, sinkNetwork.Address, payload, sinkNetwork.ReceiveFrequency, network);
+ }
+
/// <summary>
/// Helper function that invokes a port with a high/low binary logic signal.
/// </summary>
public void SendSignal(EntityUid uid, string port, bool signal, DeviceLinkSourceComponent? comp = null)
{
+ if (!Resolve(uid, ref comp))
+ return;
+
var data = new NetworkPayload
{
[DeviceNetworkConstants.LogicState] = signal ? SignalState.High : SignalState.Low
};
InvokePort(uid, port, data, comp);
+
+ comp.LastSignals[port] = signal;
+ }
+
+ /// <summary>
+ /// Clears the last signals state for linking.
+ /// This is not to be confused with sending a low signal, this is the complete absence of anything.
+ /// Use if the device is in an invalid state and has no reasonable output signal.
+ /// </summary>
+ public void ClearSignal(Entity<DeviceLinkSourceComponent?> ent, string port)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return;
+
+ ent.Comp.LastSignals.Remove(port);
}
/// <summary>
var eventArgs = new SignalReceivedEvent(port, args.Sender, args.Data);
RaiseLocalEvent(uid, ref eventArgs);
}
+
+ /// <summary>
+ /// When linking from a port that currently has a signal being sent, invoke the new link with that signal.
+ /// </summary>
+ private void OnNewLink(Entity<DeviceLinkSourceComponent> ent, ref NewLinkEvent args)
+ {
+ if (args.Source != ent.Owner)
+ return;
+
+ // only do anything if a signal is being sent from a port
+ if (!ent.Comp.LastSignals.TryGetValue(args.SourcePort, out var signal))
+ return;
+
+ var payload = new NetworkPayload()
+ {
+ [DeviceNetworkConstants.LogicState] = signal ? SignalState.High : SignalState.Low
+ };
+ InvokeDirect(ent, args.Sink, args.SourcePort, args.SinkPort, payload);
+ }
#endregion
}
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
namespace Content.Shared.DeviceLinking;
/// <summary>
/// The ports the device link source sends signals from
/// </summary>
- [DataField("ports", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<SourcePortPrototype>))]
- public HashSet<string>? Ports;
+ [DataField]
+ public HashSet<ProtoId<SourcePortPrototype>>? Ports;
/// <summary>
/// A list of sink uids that got linked for each port
/// </summary>
[ViewVariables]
- public Dictionary<string, HashSet<EntityUid>> Outputs = new();
+ public Dictionary<ProtoId<SourcePortPrototype>, HashSet<EntityUid>> Outputs = new();
+
+ /// <summary>
+ /// If set to High or Low, the last signal state for a given port.
+ /// Used when linking ports of devices that are currently outputting a signal.
+ /// Only set by <c>DeviceLinkSystem.SendSignal</c>.
+ /// </summary>
+ [DataField]
+ public Dictionary<ProtoId<SourcePortPrototype>, bool> LastSignals = new();
/// <summary>
/// The list of source to sink ports for each linked sink entity for easier managing of links
/// </summary>
- [DataField("linkedPorts")]
- public Dictionary<EntityUid, HashSet<(string source, string sink)>> LinkedPorts = new();
+ [DataField]
+ public Dictionary<EntityUid, HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)>> LinkedPorts = new();
/// <summary>
/// Limits the range devices can be linked across.
/// </summary>
- [DataField("range")]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public float Range = 30f;
}
-using Content.Shared.DeviceLinking;
+using Content.Shared.DeviceLinking;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork;
{
public readonly List<SourcePortPrototype> Sources;
public readonly List<SinkPortPrototype> Sinks;
- public readonly HashSet<(string source, string sink)> Links;
+ public readonly HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> Links;
public readonly List<(string source, string sink)>? Defaults;
public readonly string SourceAddress;
public readonly string SinkAddress;
- public DeviceLinkUserInterfaceState(List<SourcePortPrototype> sources, List<SinkPortPrototype> sinks, HashSet<(string source, string sink)> links, string sourceAddress, string sinkAddress, List<(string source, string sink)>? defaults = default)
+ public DeviceLinkUserInterfaceState(
+ List<SourcePortPrototype> sources,
+ List<SinkPortPrototype> sinks,
+ HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> links,
+ string sourceAddress,
+ string sinkAddress,
+ List<(string source, string sink)>? defaults = default)
{
Links = links;
SourceAddress = sourceAddress;