]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
make linking logic gates 1000% better (#25041)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Mon, 12 Feb 2024 06:45:51 +0000 (06:45 +0000)
committerGitHub <noreply@github.com>
Mon, 12 Feb 2024 06:45:51 +0000 (23:45 -0700)
* make door status use SendSignal

* LastSignals and logic, add ClearSignal api too

* make everything outputting a logic signal default to false

* refactor ops

* :trollface:

* :trollface:

* protoid for LastSignals

* oop

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
12 files changed:
Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs
Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs
Content.Server/DeviceLinking/Systems/DoorSignalControlSystem.cs
Content.Shared/DeviceLinking/DeviceLinkSourceComponent.cs
Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs
Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs
Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml
Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml
Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml
Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml
Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml
Resources/Prototypes/Entities/Structures/gates.yml

index 20f1fb8d7cd42c8dd0ea457b20e39bd9945cb995..c04b42f249bcc4e4a97a3d177dec9c63b6db17d4 100644 (file)
@@ -7,6 +7,7 @@ using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
 
 namespace Content.Client.NetworkConfigurator;
 
@@ -160,7 +161,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
     /// </summary>
     private sealed class LinksRender : Control
     {
-        public readonly List<(string, string)> Links = new();
+        public readonly List<(ProtoId<SourcePortPrototype>, ProtoId<SinkPortPrototype>)> Links = new();
         public readonly Dictionary<string, Button> SourceButtons = new();
         public readonly Dictionary<string, Button> SinkButtons = new();
         private readonly BoxContainer _leftButtonContainer;
index e54e21316f25ed922f0eb8de371f1b6f1d273fe2..f708237480f12cd7c5d04dc6195bf507d17341fd 100644 (file)
@@ -1,9 +1,10 @@
-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;
@@ -15,7 +16,9 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
     public override void Initialize()
     {
         base.Initialize();
+
         SubscribeLocalEvent<DeviceLinkSinkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
+        SubscribeLocalEvent<DeviceLinkSourceComponent, NewLinkEvent>(OnNewLink);
     }
 
     public override void Update(float frameTime)
@@ -51,61 +54,87 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
 
             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>
@@ -120,5 +149,24 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
         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
 }
index 40feda32f2b0c1075d250944b72726c23586222a..1c0c9713cf112c17e419515edcfd1608f2761d36 100644 (file)
@@ -85,23 +85,18 @@ namespace Content.Server.DeviceLinking.Systems
 
         private void OnStateChanged(EntityUid uid, DoorSignalControlComponent door, DoorStateChangedEvent args)
         {
-            var data = new NetworkPayload()
-            {
-                { DeviceNetworkConstants.LogicState, SignalState.Momentary }
-            };
-
             if (args.State == DoorState.Closed)
             {
-                data[DeviceNetworkConstants.LogicState] = SignalState.Low;
-                _signalSystem.InvokePort(uid, door.OutOpen, data);
+                // only ever say the door is closed when it is completely airtight
+                _signalSystem.SendSignal(uid, door.OutOpen, false);
             }
             else if (args.State == DoorState.Open
                   || args.State == DoorState.Opening
                   || args.State == DoorState.Closing
                   || args.State == DoorState.Emagging)
             {
-                data[DeviceNetworkConstants.LogicState] = SignalState.High;
-                _signalSystem.InvokePort(uid, door.OutOpen, data);
+                // say the door is open whenever it would be letting air pass
+                _signalSystem.SendSignal(uid, door.OutOpen, true);
             }
         }
     }
index 3a5d4d9f6dad205f013ae9de0af30fcdebf143cd..332eff23cb2df688b4c88ca3b445864613da3d89 100644 (file)
@@ -1,5 +1,5 @@
-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;
 
@@ -11,25 +11,32 @@ public sealed partial class DeviceLinkSourceComponent : Component
     /// <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;
 }
index 9610e2e606599e24804a1e0cf0c9ff8880e08cb2..2ac525d154dd977af90acdf86d10fd6ec71fb026 100644 (file)
@@ -153,7 +153,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
             return;
 
         var comp = EnsureComp<DeviceLinkSourceComponent>(uid);
-        comp.Ports ??= new HashSet<string>();
+        comp.Ports ??= new HashSet<ProtoId<SourcePortPrototype>>();
 
         foreach (var port in ports)
         {
@@ -233,10 +233,10 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
     /// Returns the links of a source
     /// </summary>
     /// <returns>A list of sink and source port ids that are linked together</returns>
-    public HashSet<(string source, string sink)> GetLinks(EntityUid sourceUid, EntityUid sinkUid, DeviceLinkSourceComponent? sourceComponent = null)
+    public HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> GetLinks(EntityUid sourceUid, EntityUid sinkUid, DeviceLinkSourceComponent? sourceComponent = null)
     {
         if (!Resolve(sourceUid, ref sourceComponent) || !sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var links))
-            return new HashSet<(string source, string sink)>();
+            return new HashSet<(ProtoId<SourcePortPrototype>, ProtoId<SinkPortPrototype>)>();
 
         return links;
     }
index d12456dc52617396ce433cf58fcddecedc06d050..13000d00c9a2d487183c420db7537c7a678d9183 100644 (file)
@@ -1,4 +1,5 @@
-using Content.Shared.DeviceLinking;
+using Content.Shared.DeviceLinking;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.DeviceNetwork;
@@ -30,12 +31,18 @@ public sealed class DeviceLinkUserInterfaceState : BoundUserInterfaceState
 {
     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;
index 9930e6631de4b33d0247d59649ec960b5675aae7..a185d453533b6eed376232543e9923d7e39efa86 100644 (file)
@@ -95,6 +95,8 @@
   - type: DeviceLinkSource
     ports:
       - DoorStatus
+    lastSignals:
+      DoorStatus: false
   - type: SoundOnOverload
   - type: SpawnOnOverload
   - type: UserInterface
index f4a5debeac9d6ce836443e2cf236910d1daaeb96..87fd8ef0106ba79bdfc55e84fd499170d5364fcd 100644 (file)
@@ -90,6 +90,8 @@
   - type: DeviceLinkSource
     ports:
     - DoorStatus
+    lastSignals:
+      DoorStatus: false
   - type: InteractionPopup
     interactSuccessString: comp-window-knock
     messagePerceivedByOthers: comp-window-knock
index 4281177b4b95cdd4a524e8ca14a17b7999ff6b50..e5eebab861f1c1ea47a9c0420e7a534739a91e99 100644 (file)
@@ -59,6 +59,8 @@
   - type: DeviceLinkSource
     ports:
     - DoorStatus
+    lastSignals:
+      DoorStatus: false
   - type: Damageable
     damageContainer: Inorganic
     damageModifierSet: Glass
index 80a8e8ea202b6b2034028766778e1269d13d1436..62f2000593b2c2ac520b8dc8561ab7e16cc99134 100644 (file)
     - AirDanger
     - AirWarning
     - AirNormal
+    lastSignals:
+      AirDanger: false
+      AirWarning: false
+      AirNormal: false
   - type: AtmosAlarmable
     syncWith:
     - AirAlarm
index 1b285348d5b12ac126c5fbd3b5747205d6f68311..3f77c248601722bd1ff8654c73f7b19019f65837 100644 (file)
@@ -34,6 +34,8 @@
     - On
     - Off
     - Status
+    lastSignals:
+      Status: false
 
 - type: entity
   id: SignalButton
index 8fccb39b71c83de69543e43ad596982621244188..22ab745a1eec88691a6e28be5d67d6a34d7e3b74 100644 (file)
@@ -34,6 +34,8 @@
   - type: DeviceLinkSource
     ports:
     - Output
+    lastSignals:
+      Output: false
   - type: Construction
     graph: LogicGate
     node: logic_gate
@@ -65,6 +67,9 @@
     ports:
     - OutputHigh
     - OutputLow
+    lastSignals:
+      OutputHigh: false
+      OutputLow: false
   - type: Construction
     graph: LogicGate
     node: edge_detector
     ports:
     - PowerCharging
     - PowerDischarging
+    lastSignals:
+      PowerCharging: false
+      PowerDischarging: false
   - type: Construction
     graph: LogicGate
     node: power_sensor