Device Linking and better linking ui (#13645)

Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
Co-authored-by: Visne <39844191+Visne@users.noreply.github.com>
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Julian Giebel
2023-05-07 08:07:24 +02:00
committed by GitHub
parent 2fe7055de6
commit 6ebd784cb6
100 changed files with 2096 additions and 342 deletions

View File

@@ -1,5 +1,5 @@
using Content.Shared.DeviceNetwork; using Content.Client.NetworkConfigurator.Systems;
using JetBrains.Annotations; using Content.Shared.DeviceNetwork;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
@@ -8,17 +8,18 @@ namespace Content.Client.NetworkConfigurator;
public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
{ {
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
private NetworkConfiguratorListMenu? _listMenu; private NetworkConfiguratorListMenu? _listMenu;
private NetworkConfiguratorConfigurationMenu? _configurationMenu; private NetworkConfiguratorConfigurationMenu? _configurationMenu;
private NetworkConfiguratorLinkMenu? _linkMenu;
private NetworkConfiguratorSystem _netConfig; private NetworkConfiguratorSystem _netConfig;
private DeviceListSystem _deviceList;
public NetworkConfiguratorBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) public NetworkConfiguratorBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_netConfig = _entityManager.System<NetworkConfiguratorSystem>(); _netConfig = _entityManager.System<NetworkConfiguratorSystem>();
_deviceList = _entityManager.System<DeviceListSystem>();
} }
public void OnRemoveButtonPressed(string address) public void OnRemoveButtonPressed(string address)
@@ -36,7 +37,7 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
_listMenu = new NetworkConfiguratorListMenu(this); _listMenu = new NetworkConfiguratorListMenu(this);
_listMenu.OnClose += Close; _listMenu.OnClose += Close;
_listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed(); _listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed();
_listMenu.OpenCentered(); _listMenu.OpenCenteredRight();
break; break;
case NetworkConfiguratorUiKey.Configure: case NetworkConfiguratorUiKey.Configure:
_configurationMenu = new NetworkConfiguratorConfigurationMenu(); _configurationMenu = new NetworkConfiguratorConfigurationMenu();
@@ -50,6 +51,11 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
_configurationMenu.Show.Pressed = _netConfig.ConfiguredListIsTracked(Owner.Owner); _configurationMenu.Show.Pressed = _netConfig.ConfiguredListIsTracked(Owner.Owner);
_configurationMenu.OpenCentered(); _configurationMenu.OpenCentered();
break; break;
case NetworkConfiguratorUiKey.Link:
_linkMenu = new NetworkConfiguratorLinkMenu(this);
_linkMenu.OnClose += Close;
_linkMenu.OpenCentered();
break;
} }
} }
@@ -70,6 +76,9 @@ public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
case DeviceListUserInterfaceState listState: case DeviceListUserInterfaceState listState:
_configurationMenu?.UpdateState(listState); _configurationMenu?.UpdateState(listState);
break; break;
case DeviceLinkUserInterfaceState linkState:
_linkMenu?.UpdateState(linkState);
break;
} }
} }

View File

@@ -1,7 +1,7 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator" xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="350 100"> Title="{Loc 'network-configurator-title-device-configuration'}" MinSize="350 100">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" /> <networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
@@ -15,6 +15,6 @@
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/> <Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
<Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/> <Button Name="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer> </BoxContainer>
<Label Name="Count" HorizontalAlignment="Right" /> <Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer> </BoxContainer>
</controls:FancyWindow> </controls:FancyWindow>

View File

@@ -0,0 +1,47 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="620 400" RectClipContent="True">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Margin="12 0" Text="{Loc signal-port-selector-help}"/>
</BoxContainer>
<Control VerticalExpand="True" HorizontalExpand="True" Margin="12 6 12 0">
<PanelContainer Name="MainPanel" HorizontalExpand="False" VerticalExpand="True" />
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True" Margin="6">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.3">
<RichTextLabel Name="HeaderLeft"/>
<BoxContainer Name="ButtonContainerLeft" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"/>
</BoxContainer>
<BoxContainer Name="MiddleContainer" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" SizeFlagsStretchRatio="0.2"/>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="0.3">
<RichTextLabel Name="HeaderRight"/>
<BoxContainer Name="ButtonContainerRight" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
</ScrollContainer>
</Control>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="12 4">
<BoxContainer Orientation="Horizontal">
<Button Name="ButtonClear" SetHeight="32" StyleClasses="OpenRight" Text="{Loc signal-port-selector-menu-clear}"/>
<Button Name="ButtonLinkDefault" SetHeight="32" StyleClasses="OpenLeft" Text="{Loc signal-port-selector-menu-link-defaults}"/>
</BoxContainer>
<Control HorizontalExpand="True"/>
<Control>
<Button Name="ButtonOk" MinHeight="26" StyleClasses="ButtonColorGreen" Text="{Loc signal-port-selector-menu-done}"></Button>
</Control>
</BoxContainer>
<Control SetHeight="28" Margin="1 0 2 1">
<PanelContainer Name="FooterPanel"></PanelContainer>
<BoxContainer Name="ContentFooter" HorizontalExpand="True" SetHeight="28">
<Label Text="Net#Link ™" VerticalAlignment="Center" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Label Name="AddressLabel" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Control HorizontalExpand="True"/>
<Label Name="FromAddressLabel" Margin="6 0" StyleClasses="PDAContentFooterText"/>
<Label SetWidth="25" StyleClasses="PDAContentFooterText" Align="Center" Text="➝"/><!--Turn this into an arrow texture-->
<Label Name="ToAddressLabel" Margin="6 0" StyleClasses="PDAContentFooterText"/>
</BoxContainer>
</Control>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -0,0 +1,221 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Vector2 = Robust.Shared.Maths.Vector2;
namespace Content.Client.NetworkConfigurator;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
{
private const string PanelBgColor = "#202023";
private readonly LinksRender _links;
private readonly List<SourcePortPrototype> _sources = new();
private readonly List<SinkPortPrototype> _sinks = new();
private readonly NetworkConfiguratorBoundUserInterface _userInterface;
private (ButtonPosition position, string id, int index)? _selectedButton;
private List<(string left, string right)>? _defaults;
public NetworkConfiguratorLinkMenu(NetworkConfiguratorBoundUserInterface userInterface)
{
_userInterface = userInterface;
RobustXamlLoader.Load(this);
var footerStyleBox = new StyleBoxFlat()
{
BorderThickness = new Thickness(0, 2, 0, 0),
BorderColor = Color.FromHex("#5A5A5A")
};
FooterPanel.PanelOverride = footerStyleBox;
MainPanel.PanelOverride = new StyleBoxFlat(Color.FromHex(PanelBgColor));
ButtonClear.AddStyleClass("ButtonColorRed");
ButtonLinkDefault.Disabled = true;
_links = new LinksRender(ButtonContainerLeft, ButtonContainerRight);
_links.VerticalExpand = true;
MiddleContainer.AddChild(_links);
ButtonOk.OnPressed += _ => Close();
ButtonLinkDefault.OnPressed += _ => LinkDefaults();
ButtonClear.OnPressed += _ => _userInterface.SendMessage(new NetworkConfiguratorClearLinksMessage());
}
public void UpdateState(DeviceLinkUserInterfaceState linkState)
{
ButtonContainerLeft.RemoveAllChildren();
ButtonContainerRight.RemoveAllChildren();
_sources.Clear();
_sources.AddRange(linkState.Sources);
_links.SourceButtons.Clear();
var i = 0;
foreach (var source in _sources)
{
var button = CreateButton(ButtonPosition.Left, source.Name, source.Description, source.ID, i);
ButtonContainerLeft.AddChild(button);
_links.SourceButtons.Add(source.ID, button);
i++;
}
_sinks.Clear();
_sinks.AddRange(linkState.Sinks);
_links.SinkButtons.Clear();
i = 0;
foreach (var sink in _sinks)
{
var button = CreateButton(ButtonPosition.Right, sink.Name, sink.Description, sink.ID, i);
ButtonContainerRight.AddChild(button);
_links.SinkButtons.Add(sink.ID, button);
i++;
}
_links.Links.Clear();
_links.Links.AddRange(linkState.Links);
_defaults = linkState.Defaults;
ButtonLinkDefault.Disabled = _defaults == default;
FromAddressLabel.Text = linkState.SourceAddress;
ToAddressLabel.Text = linkState.SinkAddress;
}
private void LinkDefaults()
{
if (_defaults == default)
return;
_userInterface.SendMessage(new NetworkConfiguratorLinksSaveMessage(_defaults));
}
private Button CreateButton(ButtonPosition position, string name, string description, string id, int index)
{
var button = new Button();
button.AddStyleClass("OpenBoth");
button.Text = Loc.GetString(name);
button.ToolTip = Loc.GetString(description);
button.ToggleMode = true;
button.OnPressed += args => OnButtonPressed(args, position, id, index);
return button;
}
private void OnButtonPressed(BaseButton.ButtonEventArgs args, ButtonPosition position, string id, int index)
{
var key = (position, id, index);
if (_selectedButton == key)
{
args.Button.Pressed = false;
_selectedButton = null;
return;
}
if (!_selectedButton.HasValue)
{
args.Button.Pressed = true;
_selectedButton = key;
return;
}
if (_selectedButton.Value.position == position)
{
args.Button.Pressed = false;
return;
}
var left = _selectedButton.Value.position == ButtonPosition.Left ? _selectedButton.Value.id : id;
var right = _selectedButton.Value.position == ButtonPosition.Left ? id : _selectedButton.Value.id;
_userInterface.SendMessage(new NetworkConfiguratorToggleLinkMessage(left, right));
args.Button.Pressed = false;
var container = _selectedButton.Value.position == ButtonPosition.Left ? ButtonContainerLeft : ButtonContainerRight;
if (container.GetChild(_selectedButton.Value.index) is Button button)
button.Pressed = false;
_selectedButton = null;
}
private enum ButtonPosition
{
Left,
Right
}
/// <summary>
/// Draws lines between linked ports using bezier curve calculated with polynomial coefficients
/// See: https://youtu.be/jvPPXbo87ds?t=351
/// </summary>
private sealed class LinksRender : Control
{
public readonly List<(string, string)> Links = new();
public readonly Dictionary<string, Button> SourceButtons = new();
public readonly Dictionary<string, Button> SinkButtons = new();
private readonly BoxContainer _leftButtonContainer;
private readonly BoxContainer _rightButtonContainer;
public LinksRender(BoxContainer leftButtonContainer, BoxContainer rightButtonContainer)
{
_leftButtonContainer = leftButtonContainer;
_rightButtonContainer = rightButtonContainer;
}
protected override void Draw(DrawingHandleScreen handle)
{
foreach (var (left, right) in Links)
{
if (!SourceButtons.TryGetValue(left, out var leftChild) || !SinkButtons.TryGetValue(right, out var rightChild))
continue;
var leftOffset = _leftButtonContainer.PixelPosition.Y;
var rightOffset = _rightButtonContainer.PixelPosition.Y;
var y1 = leftChild.PixelPosition.Y + leftChild.PixelHeight / 2 + leftOffset;
var y2 = rightChild.PixelPosition.Y + rightChild.PixelHeight / 2 + rightOffset;
if (left == right)
{
handle.DrawLine((0, y1), (PixelWidth, y2), Color.Cyan);
continue;
}
var controls = new List<Vector2>
{
new(0, y1),
new(30, y1),
new(PixelWidth - 30, y2),
new(PixelWidth, y2),
};
//Calculate coefficients
var c0 = controls[0];
var c1 = controls[0] * -3 + controls[1] * 3;
var c2 = controls[0] * 3 + controls[1] * -6 + controls[2] * 3;
var c3 = controls[0] * -1 + controls[1] * 3 + controls[2] * -3 + controls[3];
var points = new List<Vector2>();
//Calculate points using coefficients
for (float t = 0; t <= 1; t += 0.0001f)
{
var point = c0 + c1 * t + c2 * (t * t) + c3 * (t * t * t);
points.Add(point);
}
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, points.ToArray(), Color.Cyan);
}
}
}
}

View File

@@ -1,4 +1,6 @@
using Content.Client.NetworkConfigurator.Systems;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Random; using Robust.Shared.Random;

View File

@@ -1,7 +1,7 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator" xmlns:networkConfigurator="clr-namespace:Content.Client.NetworkConfigurator"
Title="Network Configurator" MinSize="220 400"> Title="{Loc 'network-configurator-title-saved-devices'}" MinSize="220 400">
<BoxContainer Orientation="Vertical" VerticalExpand="True"> <BoxContainer Orientation="Vertical" VerticalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" /> <networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" />
<BoxContainer Orientation="Horizontal" Margin="8 8 8 8"> <BoxContainer Orientation="Horizontal" Margin="8 8 8 8">

View File

@@ -1,8 +1,6 @@
using System.Linq;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Robust.Client.Graphics;
namespace Content.Client.NetworkConfigurator; namespace Content.Client.NetworkConfigurator.Systems;
public sealed class DeviceListSystem : SharedDeviceListSystem public sealed class DeviceListSystem : SharedDeviceListSystem
{ {

View File

@@ -1,15 +1,22 @@
using System.Linq; using System.Linq;
using Content.Client.Actions; using Content.Client.Actions;
using Content.Shared.Actions; using Content.Client.Items;
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Shared.Actions.ActionTypes; using Content.Shared.Actions.ActionTypes;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Input;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Timing;
namespace Content.Client.NetworkConfigurator; namespace Content.Client.NetworkConfigurator.Systems;
public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
{ {
@@ -17,6 +24,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
[Dependency] private readonly IOverlayManager _overlay = default!; [Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private const string Action = "ClearNetworkLinkOverlays"; private const string Action = "ClearNetworkLinkOverlays";
@@ -25,6 +33,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ClearAllOverlaysEvent>(_ => ClearAllOverlays()); SubscribeLocalEvent<ClearAllOverlaysEvent>(_ => ClearAllOverlays());
SubscribeLocalEvent<NetworkConfiguratorComponent, ItemStatusCollectMessage>(OnCollectItemStatus);
}
private void OnCollectItemStatus(EntityUid uid, NetworkConfiguratorComponent configurator, ItemStatusCollectMessage args)
{
_inputManager.TryGetKeyBinding((ContentKeyFunctions.AltUseItemInHand), out var binding);
args.Controls.Add(new StatusControl(configurator, binding?.GetKeyString() ?? ""));
} }
public bool ConfiguredListIsTracked(EntityUid uid, NetworkConfiguratorComponent? component = null) public bool ConfiguredListIsTracked(EntityUid uid, NetworkConfiguratorComponent? component = null)
@@ -102,6 +117,41 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
component.ActiveDeviceList = list; component.ActiveDeviceList = list;
} }
private sealed class StatusControl : Control
{
private readonly RichTextLabel _label;
private readonly NetworkConfiguratorComponent _configurator;
private readonly string _keyBindingName;
private bool? _linkModeActive = null;
public StatusControl(NetworkConfiguratorComponent configurator, string keyBindingName)
{
_configurator = configurator;
_keyBindingName = keyBindingName;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_linkModeActive != null && _linkModeActive == _configurator.LinkModeActive)
return;
_linkModeActive = _configurator.LinkModeActive;
var modeLocString = _linkModeActive??false
? "network-configurator-examine-mode-link"
: "network-configurator-examine-mode-list";
_label.SetMarkup(Loc.GetString("network-configurator-item-status-label",
("mode", Loc.GetString(modeLocString)),
("keybinding", _keyBindingName)));
}
}
} }
public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand

View File

@@ -82,7 +82,6 @@ namespace Content.IntegrationTests.Tests
await server.WaitPost(() => await server.WaitPost(() =>
{ {
entityMan = IoCManager.Resolve<IEntityManager>(); entityMan = IoCManager.Resolve<IEntityManager>();
var mapManager = IoCManager.Resolve<IMapManager>();
var prototypeMan = IoCManager.Resolve<IPrototypeManager>(); var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
var protoIds = prototypeMan var protoIds = prototypeMan

View File

@@ -1,12 +1,13 @@
using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Binary.Components;
using Content.Server.MachineLinking.Events; using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.MachineLinking.System; using Content.Server.MachineLinking.System;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems; namespace Content.Server.Atmos.Piping.Binary.EntitySystems;
public sealed class SignalControlledValveSystem : EntitySystem public sealed class SignalControlledValveSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signal = default!; [Dependency] private readonly DeviceLinkSystem _signal = default!;
[Dependency] private readonly GasValveSystem _valve = default!; [Dependency] private readonly GasValveSystem _valve = default!;
public override void Initialize() public override void Initialize()
@@ -19,10 +20,10 @@ public sealed class SignalControlledValveSystem : EntitySystem
private void OnInit(EntityUid uid, SignalControlledValveComponent comp, ComponentInit args) private void OnInit(EntityUid uid, SignalControlledValveComponent comp, ComponentInit args)
{ {
_signal.EnsureReceiverPorts(uid, comp.OpenPort, comp.ClosePort, comp.TogglePort); _signal.EnsureSinkPorts(uid, comp.OpenPort, comp.ClosePort, comp.TogglePort);
} }
private void OnSignalReceived(EntityUid uid, SignalControlledValveComponent comp, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, SignalControlledValveComponent comp, ref SignalReceivedEvent args)
{ {
if (!TryComp<GasValveComponent>(uid, out var valve)) if (!TryComp<GasValveComponent>(uid, out var valve))
return; return;

View File

@@ -1,13 +1,12 @@
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Monitor.Components;
using Content.Server.Atmos.Monitor.Systems; using Content.Server.Atmos.Monitor.Systems;
using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.Unary.Components; using Content.Server.Atmos.Piping.Unary.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems; using Content.Server.DeviceNetwork.Systems;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
using Content.Server.NodeContainer; using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes; using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components; using Content.Server.Power.Components;
@@ -28,7 +27,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
{ {
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -224,10 +223,10 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
private void OnInit(EntityUid uid, GasVentPumpComponent component, ComponentInit args) private void OnInit(EntityUid uid, GasVentPumpComponent component, ComponentInit args)
{ {
if (component.CanLink) if (component.CanLink)
_signalSystem.EnsureReceiverPorts(uid, component.PressurizePort, component.DepressurizePort); _signalSystem.EnsureSinkPorts(uid, component.PressurizePort, component.DepressurizePort);
} }
private void OnSignalReceived(EntityUid uid, GasVentPumpComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, GasVentPumpComponent component, ref SignalReceivedEvent args)
{ {
if (!component.CanLink) if (!component.CanLink)
return; return;

View File

@@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using Content.Server.Access.Systems; using Content.Server.Access.Systems;
using Content.Server.Cargo.Components; using Content.Server.Cargo.Components;
using Content.Server.Labels.Components; using Content.Server.Labels.Components;
using Content.Server.MachineLinking.System; using Content.Server.DeviceLinking.Systems;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
@@ -34,7 +34,7 @@ namespace Content.Server.Cargo.Systems
[Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly SignalLinkerSystem _linker = default!; [Dependency] private readonly DeviceLinkSystem _linker = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;

View File

@@ -74,7 +74,7 @@ public sealed partial class CargoSystem
private void OnInit(EntityUid uid, CargoTelepadComponent telepad, ComponentInit args) private void OnInit(EntityUid uid, CargoTelepadComponent telepad, ComponentInit args)
{ {
_linker.EnsureReceiverPorts(uid, telepad.ReceiverPort); _linker.EnsureSinkPorts(uid, telepad.ReceiverPort);
} }
private void SetEnabled(CargoTelepadComponent component, ApcPowerReceiverComponent? receiver = null, private void SetEnabled(CargoTelepadComponent component, ApcPowerReceiverComponent? receiver = null,

View File

@@ -4,11 +4,10 @@ using Robust.Shared.Timing;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.Cloning.Components; using Content.Server.Cloning.Components;
using Content.Server.MachineLinking.Components; using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -16,7 +15,8 @@ using Robust.Server.Player;
using Content.Shared.Cloning.CloningConsole; using Content.Shared.Cloning.CloningConsole;
using Content.Shared.Cloning; using Content.Shared.Cloning;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.MachineLinking.Events; using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
@@ -26,7 +26,7 @@ namespace Content.Server.Cloning
[UsedImplicitly] [UsedImplicitly]
public sealed class CloningConsoleSystem : EntitySystem public sealed class CloningConsoleSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly CloningSystem _cloningSystem = default!; [Dependency] private readonly CloningSystem _cloningSystem = default!;
@@ -48,7 +48,7 @@ namespace Content.Server.Cloning
private void OnInit(EntityUid uid, CloningConsoleComponent component, ComponentInit args) private void OnInit(EntityUid uid, CloningConsoleComponent component, ComponentInit args)
{ {
_signalSystem.EnsureTransmitterPorts(uid, CloningConsoleComponent.ScannerPort, CloningConsoleComponent.PodPort); _signalSystem.EnsureSourcePorts(uid, CloningConsoleComponent.ScannerPort, CloningConsoleComponent.PodPort);
} }
private void OnButtonPressed(EntityUid uid, CloningConsoleComponent consoleComponent, UiButtonPressedMessage args) private void OnButtonPressed(EntityUid uid, CloningConsoleComponent consoleComponent, UiButtonPressedMessage args)
{ {
@@ -72,20 +72,20 @@ namespace Content.Server.Cloning
private void OnMapInit(EntityUid uid, CloningConsoleComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, CloningConsoleComponent component, MapInitEvent args)
{ {
if (!TryComp<SignalTransmitterComponent>(uid, out var receiver)) if (!TryComp<DeviceLinkSourceComponent>(uid, out var receiver))
return; return;
foreach (var port in receiver.Outputs.Values.SelectMany(ports => ports)) foreach (var port in receiver.Outputs.Values.SelectMany(ports => ports))
{ {
if (TryComp<MedicalScannerComponent>(port.Uid, out var scanner)) if (TryComp<MedicalScannerComponent>(port, out var scanner))
{ {
component.GeneticScanner = port.Uid; component.GeneticScanner = port;
scanner.ConnectedConsole = uid; scanner.ConnectedConsole = uid;
} }
if (TryComp<CloningPodComponent>(port.Uid, out var pod)) if (TryComp<CloningPodComponent>(port, out var pod))
{ {
component.CloningPod = port.Uid; component.CloningPod = port;
pod.ConnectedConsole = uid; pod.ConnectedConsole = uid;
} }
} }
@@ -93,15 +93,15 @@ namespace Content.Server.Cloning
private void OnNewLink(EntityUid uid, CloningConsoleComponent component, NewLinkEvent args) private void OnNewLink(EntityUid uid, CloningConsoleComponent component, NewLinkEvent args)
{ {
if (TryComp<MedicalScannerComponent>(args.Receiver, out var scanner) && args.TransmitterPort == CloningConsoleComponent.ScannerPort) if (TryComp<MedicalScannerComponent>(args.Sink, out var scanner) && args.SourcePort == CloningConsoleComponent.ScannerPort)
{ {
component.GeneticScanner = args.Receiver; component.GeneticScanner = args.Sink;
scanner.ConnectedConsole = uid; scanner.ConnectedConsole = uid;
} }
if (TryComp<CloningPodComponent>(args.Receiver, out var pod) && args.TransmitterPort == CloningConsoleComponent.PodPort) if (TryComp<CloningPodComponent>(args.Sink, out var pod) && args.SourcePort == CloningConsoleComponent.PodPort)
{ {
component.CloningPod = args.Receiver; component.CloningPod = args.Sink;
pod.ConnectedConsole = uid; pod.ConnectedConsole = uid;
} }
RecheckConnections(uid, component.CloningPod, component.GeneticScanner, component); RecheckConnections(uid, component.CloningPod, component.GeneticScanner, component);

View File

@@ -10,15 +10,15 @@ using Content.Server.Power.EntitySystems;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server.Humanoid; using Content.Server.Humanoid;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Stack;
using Content.Server.Jobs; using Content.Server.Jobs;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes; using Content.Shared.Humanoid.Prototypes;
@@ -43,7 +43,7 @@ namespace Content.Server.Cloning
{ {
public sealed class CloningSystem : EntitySystem public sealed class CloningSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly IPlayerManager _playerManager = null!; [Dependency] private readonly IPlayerManager _playerManager = null!;
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly EuiManager _euiManager = null!; [Dependency] private readonly EuiManager _euiManager = null!;
@@ -84,7 +84,7 @@ namespace Content.Server.Cloning
private void OnComponentInit(EntityUid uid, CloningPodComponent clonePod, ComponentInit args) private void OnComponentInit(EntityUid uid, CloningPodComponent clonePod, ComponentInit args)
{ {
clonePod.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(clonePod.Owner, "clonepod-bodyContainer"); clonePod.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(clonePod.Owner, "clonepod-bodyContainer");
_signalSystem.EnsureReceiverPorts(uid, CloningPodComponent.PodPort); _signalSystem.EnsureSinkPorts(uid, CloningPodComponent.PodPort);
} }
private void OnPartsRefreshed(EntityUid uid, CloningPodComponent component, RefreshPartsEvent args) private void OnPartsRefreshed(EntityUid uid, CloningPodComponent component, RefreshPartsEvent args)

View File

@@ -1,4 +1,4 @@
namespace Content.Server.MachineLinking.Components; namespace Content.Server.DeviceLinking.Components;
/// <summary> /// <summary>
/// This is used for automatic linkage with buttons and other transmitters. /// This is used for automatic linkage with buttons and other transmitters.

View File

@@ -1,4 +1,4 @@
namespace Content.Server.MachineLinking.Components; namespace Content.Server.DeviceLinking.Components;
/// <summary> /// <summary>
/// This is used for automatic linkage with various receivers, like shutters. /// This is used for automatic linkage with various receivers, like shutters.

View File

@@ -1,7 +1,7 @@
using Content.Shared.MachineLinking; using Content.Shared.MachineLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components namespace Content.Server.DeviceLinking.Components
{ {
[RegisterComponent] [RegisterComponent]
public sealed class DoorSignalControlComponent : Component public sealed class DoorSignalControlComponent : Component

View File

@@ -2,7 +2,7 @@ using Content.Shared.MachineLinking;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components namespace Content.Server.DeviceLinking.Components
{ {
/// <summary> /// <summary>
/// Simple switch that will fire ports when toggled on or off. A button is jsut a switch that signals on the /// Simple switch that will fire ports when toggled on or off. A button is jsut a switch that signals on the

View File

@@ -1,7 +1,7 @@
using Content.Shared.MachineLinking; using Content.Shared.MachineLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components namespace Content.Server.DeviceLinking.Components
{ {
/// <summary> /// <summary>
/// Sends out a signal to machine linked objects. /// Sends out a signal to machine linked objects.

View File

@@ -1,7 +1,8 @@
using Content.Shared.DeviceLinking;
using Content.Shared.MachineLinking; using Content.Shared.MachineLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components namespace Content.Server.DeviceLinking.Components
{ {
[RegisterComponent] [RegisterComponent]
public sealed class TwoWayLeverComponent : Component public sealed class TwoWayLeverComponent : Component

View File

@@ -0,0 +1,6 @@
using Content.Server.DeviceNetwork;
namespace Content.Server.DeviceLinking.Events;
[ByRefEvent]
public readonly record struct SignalReceivedEvent(string Port, EntityUid? Trigger = null, NetworkPayload? Data = null);

View File

@@ -1,13 +1,14 @@
using Content.Server.MachineLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.MachineLinking.System;
namespace Content.Server.MachineLinking.System; namespace Content.Server.DeviceLinking.Systems;
/// <summary> /// <summary>
/// This handles automatically linking autolinked entities at round-start. /// This handles automatically linking autolinked entities at round-start.
/// </summary> /// </summary>
public sealed class AutoLinkSystem : EntitySystem public sealed class AutoLinkSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalLinkerSystem = default!; [Dependency] private readonly DeviceLinkSystem _deviceLinkSystem = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -29,7 +30,7 @@ public sealed class AutoLinkSystem : EntitySystem
if (rxXform.GridUid != xform.GridUid) if (rxXform.GridUid != xform.GridUid)
continue; continue;
_signalLinkerSystem.TryLinkDefaults(receiver.Owner, uid, null); _deviceLinkSystem.LinkDefaults(null, uid, receiver.Owner);
} }
} }
} }

View File

@@ -0,0 +1,114 @@
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.MachineLinking.Components;
using Content.Shared.DeviceLinking;
using Robust.Shared.Utility;
namespace Content.Server.DeviceLinking.Systems;
public sealed class DeviceLinkSystem : SharedDeviceLinkSystem
{
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SignalTransmitterComponent, MapInitEvent>(OnTransmitterStartup);
SubscribeLocalEvent<DeviceLinkSinkComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
}
/// <summary>
/// Moves existing links from machine linking to device linking to ensure linked things still work even when the map wasn't updated yet
/// </summary>
private void OnTransmitterStartup(EntityUid sourceUid, SignalTransmitterComponent transmitterComponent, MapInitEvent args)
{
if (!TryComp<DeviceLinkSourceComponent?>(sourceUid, out var sourceComponent))
return;
Dictionary<EntityUid, List<(string, string)>> outputs = new();
foreach (var (transmitterPort, receiverPorts) in transmitterComponent.Outputs)
{
foreach (var receiverPort in receiverPorts)
{
outputs.GetOrNew(receiverPort.Uid).Add((transmitterPort, receiverPort.Port));
}
}
foreach (var (sinkUid, links) in outputs)
{
SaveLinks(null, sourceUid, sinkUid, links, sourceComponent);
}
}
#region Sending & Receiving
/// <summary>
/// Sends a network payload directed at the sink entity.
/// Just raises a <see cref="SignalReceivedEvent"/> without data if the source or the sink doesn't have a <see cref="DeviceNetworkComponent"/>
/// </summary>
/// <param name="uid">The source uid that invokes the port</param>
/// <param name="port">The port to invoke</param>
/// <param name="data">Optional data to send along</param>
/// <param name="sourceComponent"></param>
public void InvokePort(EntityUid uid, string port, NetworkPayload? data = null, DeviceLinkSourceComponent? sourceComponent = null)
{
if (!Resolve(uid, ref sourceComponent) || !sourceComponent.Outputs.TryGetValue(port, out var sinks))
return;
foreach (var sinkUid in sinks)
{
if (!sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var links))
continue;
foreach (var (source, sink) in links)
{
if (source != port)
continue;
//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);
}
}
_deviceNetworkSystem.QueuePacket(uid, sinkNetworkComponent.Address, payload, sinkNetworkComponent.ReceiveFrequency);
}
}
}
/// <summary>
/// Checks if the payload has a port defined and if the port is present on the sink.
/// Raises a <see cref="SignalReceivedEvent"/> containing the payload when the check passes
/// </summary>
private void OnPacketReceived(EntityUid uid, DeviceLinkSinkComponent component, DeviceNetworkPacketEvent args)
{
if (!args.Data.TryGetValue(InvokedPort, out string? port) || !(component.Ports?.Contains(port) ?? false))
return;
var eventArgs = new SignalReceivedEvent(port, args.Sender, args.Data);
RaiseLocalEvent(uid, ref eventArgs);
}
#endregion
}

View File

@@ -1,16 +1,17 @@
using Content.Server.MachineLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.MachineLinking.Events; using Content.Server.DeviceLinking.Events;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Server.MachineLinking.System;
using Content.Shared.Doors.Components; using Content.Shared.Doors.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Content.Server.MachineLinking.System namespace Content.Server.DeviceLinking.Systems
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class DoorSignalControlSystem : EntitySystem public sealed class DoorSignalControlSystem : EntitySystem
{ {
[Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -18,13 +19,13 @@ namespace Content.Server.MachineLinking.System
SubscribeLocalEvent<DoorSignalControlComponent, ComponentInit>(OnInit); SubscribeLocalEvent<DoorSignalControlComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<DoorSignalControlComponent, SignalReceivedEvent>(OnSignalReceived); SubscribeLocalEvent<DoorSignalControlComponent, SignalReceivedEvent>(OnSignalReceived);
} }
private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args) private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args)
{ {
_signalSystem.EnsureReceiverPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort); _signalSystem.EnsureSinkPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort);
} }
private void OnSignalReceived(EntityUid uid, DoorSignalControlComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, DoorSignalControlComponent component, ref SignalReceivedEvent args)
{ {
if (!TryComp(uid, out DoorComponent? door)) if (!TryComp(uid, out DoorComponent? door))
return; return;

View File

@@ -1,14 +1,15 @@
using Content.Server.MachineLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Server.MachineLinking.System;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
namespace Content.Server.MachineLinking.System namespace Content.Server.DeviceLinking.Systems
{ {
public sealed class SignalSwitchSystem : EntitySystem public sealed class SignalSwitchSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -20,7 +21,7 @@ namespace Content.Server.MachineLinking.System
private void OnInit(EntityUid uid, SignalSwitchComponent component, ComponentInit args) private void OnInit(EntityUid uid, SignalSwitchComponent component, ComponentInit args)
{ {
_signalSystem.EnsureTransmitterPorts(uid, component.OnPort, component.OffPort); _signalSystem.EnsureSourcePorts(uid, component.OnPort, component.OffPort);
} }
private void OnActivated(EntityUid uid, SignalSwitchComponent component, ActivateInWorldEvent args) private void OnActivated(EntityUid uid, SignalSwitchComponent component, ActivateInWorldEvent args)

View File

@@ -1,13 +1,13 @@
using Content.Server.DeviceLinking.Components;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.MachineLinking.Components;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Timing; using Content.Shared.Timing;
namespace Content.Server.MachineLinking.System; namespace Content.Server.DeviceLinking.Systems;
public sealed class SignallerSystem : EntitySystem public sealed class SignallerSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signal = default!; [Dependency] private readonly DeviceLinkSystem _link = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!;
public override void Initialize() public override void Initialize()
@@ -21,14 +21,14 @@ public sealed class SignallerSystem : EntitySystem
private void OnInit(EntityUid uid, SignallerComponent component, ComponentInit args) private void OnInit(EntityUid uid, SignallerComponent component, ComponentInit args)
{ {
_signal.EnsureTransmitterPorts(uid, component.Port); _link.EnsureSourcePorts(uid, component.Port);
} }
private void OnUseInHand(EntityUid uid, SignallerComponent component, UseInHandEvent args) private void OnUseInHand(EntityUid uid, SignallerComponent component, UseInHandEvent args)
{ {
if (args.Handled) if (args.Handled)
return; return;
_signal.InvokePort(uid, component.Port); _link.InvokePort(uid, component.Port);
args.Handled = true; args.Handled = true;
} }
@@ -43,7 +43,7 @@ public sealed class SignallerSystem : EntitySystem
if (hasUseDelay) if (hasUseDelay)
_useDelay.BeginDelay(uid, useDelay); _useDelay.BeginDelay(uid, useDelay);
_signal.InvokePort(uid, component.Port); _link.InvokePort(uid, component.Port);
args.Handled = true; args.Handled = true;
} }
} }

View File

@@ -1,15 +1,14 @@
using Content.Server.MachineLinking.Components; using Content.Server.DeviceLinking.Components;
using Content.Shared.DeviceLinking;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.MachineLinking;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.MachineLinking.System namespace Content.Server.DeviceLinking.Systems
{ {
public sealed class TwoWayLeverSystem : EntitySystem public sealed class TwoWayLeverSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
const string _leftToggleImage = "rotate_ccw.svg.192dpi.png"; const string _leftToggleImage = "rotate_ccw.svg.192dpi.png";
@@ -25,7 +24,7 @@ namespace Content.Server.MachineLinking.System
private void OnInit(EntityUid uid, TwoWayLeverComponent component, ComponentInit args) private void OnInit(EntityUid uid, TwoWayLeverComponent component, ComponentInit args)
{ {
_signalSystem.EnsureTransmitterPorts(uid, component.LeftPort, component.RightPort, component.MiddlePort); _signalSystem.EnsureSourcePorts(uid, component.LeftPort, component.RightPort, component.MiddlePort);
} }
private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args) private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args)

View File

@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using JetBrains.Annotations; using JetBrains.Annotations;

View File

@@ -1,16 +1,22 @@
using System.Linq; using System.Linq;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components;
using Content.Server.UserInterface;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.DeviceNetwork.Systems; namespace Content.Server.DeviceNetwork.Systems;
@@ -19,11 +25,14 @@ namespace Content.Server.DeviceNetwork.Systems;
public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
{ {
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!; [Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
[Dependency] private readonly DeviceLinkSystem _deviceLinkSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessSystem = default!; [Dependency] private readonly AccessReaderSystem _accessSystem = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -32,17 +41,23 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
SubscribeLocalEvent<NetworkConfiguratorComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<NetworkConfiguratorComponent, MapInitEvent>(OnMapInit);
//Interaction //Interaction
SubscribeLocalEvent<NetworkConfiguratorComponent, AfterInteractEvent>((uid, component, args) => OnUsed(uid, component, args.Target, args.User, args.CanReach)); //TODO: Replace with utility verb? SubscribeLocalEvent<NetworkConfiguratorComponent, AfterInteractEvent>(AfterInteract); //TODO: Replace with utility verb?
SubscribeLocalEvent<NetworkConfiguratorComponent, ExaminedEvent>(DoExamine);
//Verbs //Verbs
SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<UtilityVerb>>(OnAddInteractVerb); SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<UtilityVerb>>(OnAddInteractVerb);
SubscribeLocalEvent<DeviceNetworkComponent, GetVerbsEvent<AlternativeVerb>>(OnAddAlternativeSaveDeviceVerb); SubscribeLocalEvent<DeviceNetworkComponent, GetVerbsEvent<AlternativeVerb>>(OnAddAlternativeSaveDeviceVerb);
SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<AlternativeVerb>>(OnAddSwitchModeVerb);
//UI //UI
SubscribeLocalEvent<NetworkConfiguratorComponent, BoundUIClosedEvent>(OnUiClosed); SubscribeLocalEvent<NetworkConfiguratorComponent, BoundUIClosedEvent>(OnUiClosed);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorRemoveDeviceMessage>(OnRemoveDevice); SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorRemoveDeviceMessage>(OnRemoveDevice);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearDevicesMessage>(OnClearDevice); SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearDevicesMessage>(OnClearDevice);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorLinksSaveMessage>(OnSaveLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearLinksMessage>(OnClearLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorToggleLinkMessage>(OnToggleLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorButtonPressedMessage>(OnConfigButtonPressed); SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorButtonPressedMessage>(OnConfigButtonPressed);
SubscribeLocalEvent<NetworkConfiguratorComponent, ActivatableUIOpenAttemptEvent>(OnUiOpenAttempt);
SubscribeLocalEvent<DeviceListComponent, ComponentRemove>(OnComponentRemoved); SubscribeLocalEvent<DeviceListComponent, ComponentRemove>(OnComponentRemoved);
} }
@@ -62,14 +77,14 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
} }
//The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device. //The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.Configure)?.CloseAll(); _uiSystem.TryCloseAll(uid, NetworkConfiguratorUiKey.Configure);
} }
} }
private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args)
{ {
component.Devices.Clear(); component.Devices.Clear();
UpdateUiState(uid, component); UpdateListUiState(uid, component);
} }
private void TryAddNetworkDevice(EntityUid? targetUid, EntityUid configuratorUid, EntityUid userUid, private void TryAddNetworkDevice(EntityUid? targetUid, EntityUid configuratorUid, EntityUid userUid,
@@ -118,18 +133,65 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
_popupSystem.PopupCursor(Loc.GetString("network-configurator-device-saved", ("address", device.Address), ("device", targetUid)), _popupSystem.PopupCursor(Loc.GetString("network-configurator-device-saved", ("address", device.Address), ("device", targetUid)),
userUid, PopupType.Medium); userUid, PopupType.Medium);
UpdateUiState(configurator.Owner, configurator); UpdateListUiState(configurator.Owner, configurator);
}
private void TryLinkDevice(EntityUid uid, NetworkConfiguratorComponent configurator, EntityUid? target, EntityUid user)
{
if (!HasComp<DeviceLinkSourceComponent>(target) && !HasComp<DeviceLinkSinkComponent>(target))
return;
if (configurator.ActiveDeviceLink == target)
{
_popupSystem.PopupEntity(Loc.GetString("network-configurator-link-mode-stopped"), target.Value, user);
configurator.ActiveDeviceLink = null;
return;
}
if (HasComp<DeviceLinkSourceComponent>(target) && HasComp<DeviceLinkSourceComponent>(configurator.ActiveDeviceLink)
|| HasComp<DeviceLinkSinkComponent>(target) && HasComp<DeviceLinkSinkComponent>(configurator.ActiveDeviceLink))
{
return;
}
if (configurator.ActiveDeviceLink.HasValue)
{
OpenDeviceLinkUi( uid, target, user, configurator);
return;
}
_popupSystem.PopupEntity(Loc.GetString("network-configurator-link-mode-started", ("device", Name(target.Value))), target.Value, user);
configurator.ActiveDeviceLink = target;
}
private void TryLinkDefaults(EntityUid uid, NetworkConfiguratorComponent configurator, EntityUid? targetUid, EntityUid user)
{
if (!configurator.LinkModeActive || !configurator.ActiveDeviceLink.HasValue
|| !targetUid.HasValue || configurator.ActiveDeviceLink == targetUid)
return;
if (!HasComp<DeviceLinkSourceComponent>(targetUid) && !HasComp<DeviceLinkSinkComponent>(targetUid))
return;
if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSourceComponent? activeSource) && TryComp(targetUid, out DeviceLinkSinkComponent? targetSink))
{
_deviceLinkSystem.LinkDefaults(user, configurator.ActiveDeviceLink.Value, targetUid.Value, activeSource, targetSink);
}
else if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSinkComponent? activeSink) && TryComp(targetUid, out DeviceLinkSourceComponent? targetSource))
{
_deviceLinkSystem.LinkDefaults(user, targetUid.Value, configurator.ActiveDeviceLink.Value, targetSource, activeSink);
}
} }
private bool AccessCheck(EntityUid target, EntityUid? user, NetworkConfiguratorComponent component) private bool AccessCheck(EntityUid target, EntityUid? user, NetworkConfiguratorComponent component)
{ {
if (!TryComp(target, out AccessReaderComponent? reader) || user == null) if (!TryComp(target, out AccessReaderComponent? reader) || user == null)
return false; return true;
if (_accessSystem.IsAllowed(user.Value, reader)) if (_accessSystem.IsAllowed(user.Value, reader))
return true; return true;
SoundSystem.Play(component.SoundNoAccess.GetSound(), Filter.Pvs(user.Value), target, AudioParams.Default.WithVolume(-2f).WithPitchScale(1.2f)); _audioSystem.PlayPvs(component.SoundNoAccess, user.Value, AudioParams.Default.WithVolume(-2f).WithPitchScale(1.2f));
_popupSystem.PopupEntity(Loc.GetString("network-configurator-device-access-denied"), target, user.Value); _popupSystem.PopupEntity(Loc.GetString("network-configurator-device-access-denied"), target, user.Value);
return false; return false;
@@ -137,26 +199,80 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnComponentRemoved(EntityUid uid, DeviceListComponent component, ComponentRemove args) private void OnComponentRemoved(EntityUid uid, DeviceListComponent component, ComponentRemove args)
{ {
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.Configure)?.CloseAll(); _uiSystem.TryCloseAll(uid, NetworkConfiguratorUiKey.Configure);
}
private void SwitchMode(EntityUid? userUid, EntityUid configuratorUid, NetworkConfiguratorComponent configurator)
{
if (Delay(configurator))
return;
configurator.LinkModeActive = !configurator.LinkModeActive;
if (!userUid.HasValue)
return;
if (!configurator.LinkModeActive)
configurator.ActiveDeviceLink = null;
var locString = configurator.LinkModeActive ? "network-configurator-mode-link" : "network-configurator-mode-list";
_popupSystem.PopupEntity(Loc.GetString("network-configurator-switched-mode", ("mode", Loc.GetString(locString))),
configuratorUid, userUid.Value);
Dirty(configurator);
_appearanceSystem.SetData(configuratorUid, NetworkConfiguratorVisuals.Mode, configurator.LinkModeActive);
var pitch = configurator.LinkModeActive ? 1 : 0.8f;
_audioSystem.PlayPvs(configurator.SoundSwitchMode, userUid.Value, AudioParams.Default.WithVolume(1.5f).WithPitchScale(pitch));
}
/// <summary>
/// Returns true if the last time this method was called is earlier than the configurators use delay.
/// </summary>
private bool Delay(NetworkConfiguratorComponent configurator)
{
var currentTime = _gameTiming.CurTime;
if (currentTime < configurator.LastUseAttempt + configurator.UseDelay)
return true;
configurator.LastUseAttempt = currentTime;
return false;
} }
#region Interactions #region Interactions
private void DoExamine(EntityUid uid, NetworkConfiguratorComponent component, ExaminedEvent args)
{
var mode = component.LinkModeActive ? "network-configurator-examine-mode-link" : "network-configurator-examine-mode-list";
args.PushMarkup(Loc.GetString("network-configurator-examine-current-mode", ("mode", Loc.GetString(mode))));
}
private void AfterInteract(EntityUid uid, NetworkConfiguratorComponent component, AfterInteractEvent args)
{
OnUsed(uid, component, args.Target, args.User, args.CanReach);
}
/// <summary> /// <summary>
/// Either adds a device to the device list or shows the config ui if the target is ant entity with a device list /// Either adds a device to the device list or shows the config ui if the target is ant entity with a device list
/// </summary> /// </summary>
private void OnUsed(EntityUid uid, NetworkConfiguratorComponent component, EntityUid? target, EntityUid user, bool canReach = true) private void OnUsed(EntityUid uid, NetworkConfiguratorComponent configurator, EntityUid? target, EntityUid user, bool canReach = true)
{ {
if (!canReach) if (!canReach || !target.HasValue)
return; return;
if (!HasComp<DeviceListComponent>(target)) if (configurator.LinkModeActive)
{ {
TryAddNetworkDevice(target, user, component); TryLinkDevice(uid, configurator, target, user);
return; return;
} }
OpenDeviceListUi(target, user, component); if (!HasComp<DeviceListComponent>(target))
{
TryAddNetworkDevice(target, user, configurator);
return;
}
OpenDeviceListUi(target, user, configurator);
} }
#endregion #endregion
@@ -166,22 +282,32 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// <summary> /// <summary>
/// Adds the interaction verb which is either configuring device lists or saving a device onto the configurator /// Adds the interaction verb which is either configuring device lists or saving a device onto the configurator
/// </summary> /// </summary>
private void OnAddInteractVerb(EntityUid uid, NetworkConfiguratorComponent component, GetVerbsEvent<UtilityVerb> args) private void OnAddInteractVerb(EntityUid uid, NetworkConfiguratorComponent configurator, GetVerbsEvent<UtilityVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue || !HasComp<DeviceNetworkComponent>(args.Target)) if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue)
return; return;
var isDeviceList = HasComp<DeviceListComponent>(args.Target); var verb = new UtilityVerb
UtilityVerb verb = new()
{ {
Text = Loc.GetString(isDeviceList ? "network-configurator-configure" : "network-configurator-save-device"), Act = () => OnUsed(uid, configurator, args.Target, args.User),
Icon = isDeviceList ?
new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")) :
new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/in.svg.192dpi.png")),
Act = () => OnUsed(uid, component, args.Target, args.User),
Impact = LogImpact.Low Impact = LogImpact.Low
}; };
if (configurator.LinkModeActive)
{
var linkStarted = configurator.ActiveDeviceLink.HasValue;
verb.Text = Loc.GetString(linkStarted ? "network-configurator-link" : "network-configurator-start-link");
verb.Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/in.svg.192dpi.png"));
}
else if (!HasComp<DeviceNetworkComponent>(args.Target))
{
var isDeviceList = HasComp<DeviceListComponent>(args.Target);
verb.Text = Loc.GetString(isDeviceList ? "network-configurator-configure" : "network-configurator-save-device");
verb.Icon = isDeviceList
? new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png"))
: new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/in.svg.192dpi.png"));
}
args.Verbs.Add(verb); args.Verbs.Add(verb);
} }
@@ -193,8 +319,39 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// </summary> /// </summary>
private void OnAddAlternativeSaveDeviceVerb(EntityUid uid, DeviceNetworkComponent component, GetVerbsEvent<AlternativeVerb> args) private void OnAddAlternativeSaveDeviceVerb(EntityUid uid, DeviceNetworkComponent component, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue || !HasComp<NetworkConfiguratorComponent>(args.Using.Value) if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue
|| !HasComp<DeviceListComponent>(args.Target)) || !TryComp<NetworkConfiguratorComponent>(args.Using.Value, out var configurator))
return;
if (!configurator.LinkModeActive && HasComp<DeviceListComponent>(args.Target))
{
AlternativeVerb verb = new()
{
Text = Loc.GetString("network-configurator-save-device"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/in.svg.192dpi.png")),
Act = () => TryAddNetworkDevice(args.Target, args.Using.Value, args.User),
Impact = LogImpact.Low
};
args.Verbs.Add(verb);
return;
}
if (configurator is {LinkModeActive: true, ActiveDeviceLink: { }} && (HasComp<DeviceLinkSinkComponent>(args.Target) || HasComp<DeviceLinkSourceComponent>(args.Target)))
{
AlternativeVerb verb = new()
{
Text = Loc.GetString("network-configurator-link-defaults"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/in.svg.192dpi.png")),
Act = () => TryLinkDefaults(args.Using.Value, configurator, args.Target, args.User),
Impact = LogImpact.Low
};
args.Verbs.Add(verb);
}
}
private void OnAddSwitchModeVerb(EntityUid uid, NetworkConfiguratorComponent configurator, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue || !HasComp<NetworkConfiguratorComponent>(args.Target))
return; return;
AlternativeVerb verb = new() AlternativeVerb verb = new()
@@ -211,11 +368,57 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
#region UI #region UI
private void OpenDeviceLinkUi(EntityUid configuratorUid, EntityUid? targetUid, EntityUid userUid, NetworkConfiguratorComponent configurator)
{
if (Delay(configurator))
return;
if (!targetUid.HasValue || !configurator.ActiveDeviceLink.HasValue || !TryComp(userUid, out ActorComponent? actor) || !AccessCheck(targetUid.Value, userUid, configurator))
return;
_uiSystem.TryOpen(configuratorUid, NetworkConfiguratorUiKey.Link, actor.PlayerSession);
configurator.DeviceLinkTarget = targetUid;
if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSourceComponent? activeSource) && TryComp(targetUid, out DeviceLinkSinkComponent? targetSink))
{
UpdateLinkUiState(configuratorUid, configurator.ActiveDeviceLink.Value, targetUid.Value, activeSource, targetSink);
}
else if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSinkComponent? activeSink)
&& TryComp(targetUid, out DeviceLinkSourceComponent? targetSource))
{
UpdateLinkUiState(configuratorUid, targetUid.Value, configurator.ActiveDeviceLink.Value, targetSource, activeSink);
}
}
private void UpdateLinkUiState(EntityUid configuratorUid, EntityUid sourceUid, EntityUid sinkUid,
DeviceLinkSourceComponent? sourceComponent = null, DeviceLinkSinkComponent? sinkComponent = null,
DeviceNetworkComponent? sourceNetworkComponent = null, DeviceNetworkComponent? sinkNetworkComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent) || !Resolve(sinkUid, ref sinkComponent))
return;
var sources = _deviceLinkSystem.GetSourcePorts(sourceUid, sourceComponent);
var sinks = _deviceLinkSystem.GetSinkPorts(sinkUid, sinkComponent);
var links = _deviceLinkSystem.GetLinks(sourceUid, sinkUid, sourceComponent);
var defaults = _deviceLinkSystem.GetDefaults(sources);
var sourceAddress = Resolve(sourceUid, ref sourceNetworkComponent) ? sourceNetworkComponent.Address : "";
var sinkAddress = Resolve(sinkUid, ref sinkNetworkComponent) ? sinkNetworkComponent.Address : "";
var state = new DeviceLinkUserInterfaceState(sources, sinks, links, sourceAddress, sinkAddress, defaults);
_uiSystem.TrySetUiState(configuratorUid, NetworkConfiguratorUiKey.Link, state);
}
/// <summary> /// <summary>
/// Opens the config ui. It can be used to modify the devices in the targets device list. /// Opens the config ui. It can be used to modify the devices in the targets device list.
/// </summary> /// </summary>
private void OpenDeviceListUi(EntityUid? targetUid, EntityUid userUid, NetworkConfiguratorComponent configurator) private void OpenDeviceListUi(EntityUid? targetUid, EntityUid userUid, NetworkConfiguratorComponent configurator)
{ {
if (Delay(configurator))
return;
if (!targetUid.HasValue || !TryComp(userUid, out ActorComponent? actor) || !AccessCheck(targetUid.Value, userUid, configurator)) if (!targetUid.HasValue || !TryComp(userUid, out ActorComponent? actor) || !AccessCheck(targetUid.Value, userUid, configurator))
return; return;
@@ -233,7 +436,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// <summary> /// <summary>
/// Sends the list of saved devices to the ui /// Sends the list of saved devices to the ui
/// </summary> /// </summary>
private void UpdateUiState(EntityUid uid, NetworkConfiguratorComponent component) private void UpdateListUiState(EntityUid uid, NetworkConfiguratorComponent component)
{ {
HashSet<(string address, string name)> devices = new(); HashSet<(string address, string name)> devices = new();
HashSet<string> invalidDevices = new(); HashSet<string> invalidDevices = new();
@@ -264,6 +467,12 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnUiClosed(EntityUid uid, NetworkConfiguratorComponent component, BoundUIClosedEvent args) private void OnUiClosed(EntityUid uid, NetworkConfiguratorComponent component, BoundUIClosedEvent args)
{ {
component.ActiveDeviceList = null; component.ActiveDeviceList = null;
if (args.UiKey is NetworkConfiguratorUiKey.Link)
{
component.ActiveDeviceLink = null;
component.DeviceLinkTarget = null;
}
} }
/// <summary> /// <summary>
@@ -272,7 +481,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnRemoveDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorRemoveDeviceMessage args) private void OnRemoveDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorRemoveDeviceMessage args)
{ {
component.Devices.Remove(args.Address); component.Devices.Remove(args.Address);
UpdateUiState(uid, component); UpdateListUiState(uid, component);
} }
/// <summary> /// <summary>
@@ -281,7 +490,61 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnClearDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorClearDevicesMessage _) private void OnClearDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorClearDevicesMessage _)
{ {
component.Devices.Clear(); component.Devices.Clear();
UpdateUiState(uid, component); UpdateListUiState(uid, component);
}
private void OnClearLinks(EntityUid uid, NetworkConfiguratorComponent configurator, NetworkConfiguratorClearLinksMessage args)
{
if (!configurator.ActiveDeviceLink.HasValue || !configurator.DeviceLinkTarget.HasValue)
return;
if (HasComp<DeviceLinkSourceComponent>(configurator.ActiveDeviceLink))
{
_deviceLinkSystem.RemoveSinkFromSource(configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value);
UpdateLinkUiState(uid, configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value);
}
else if (HasComp<DeviceLinkSourceComponent>(configurator.DeviceLinkTarget))
{
_deviceLinkSystem.RemoveSinkFromSource(configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value);
UpdateLinkUiState(uid, configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value);
}
}
private void OnToggleLinks(EntityUid uid, NetworkConfiguratorComponent configurator, NetworkConfiguratorToggleLinkMessage args)
{
if (!configurator.ActiveDeviceLink.HasValue || !configurator.DeviceLinkTarget.HasValue)
return;
if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSourceComponent? activeSource))
{
_deviceLinkSystem.ToggleLink(args.Session.AttachedEntity, configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value, args.Source, args.Sink, activeSource);
UpdateLinkUiState(uid, configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value, activeSource);
}
else if (TryComp(configurator.DeviceLinkTarget, out DeviceLinkSourceComponent? targetSource))
{
_deviceLinkSystem.ToggleLink(args.Session.AttachedEntity, configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value, args.Source, args.Sink, targetSource);
UpdateLinkUiState(uid, configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value, targetSource);
}
}
/// <summary>
/// Saves links set by the device link UI
/// </summary>
private void OnSaveLinks(EntityUid uid, NetworkConfiguratorComponent configurator, NetworkConfiguratorLinksSaveMessage args)
{
if (!configurator.ActiveDeviceLink.HasValue || !configurator.DeviceLinkTarget.HasValue)
return;
if (TryComp(configurator.ActiveDeviceLink, out DeviceLinkSourceComponent? activeSource))
{
_deviceLinkSystem.SaveLinks(args.Session.AttachedEntity, configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value, args.Links, activeSource);
UpdateLinkUiState(uid, configurator.ActiveDeviceLink.Value, configurator.DeviceLinkTarget.Value, activeSource);
}
else if (TryComp(configurator.DeviceLinkTarget, out DeviceLinkSourceComponent? targetSource))
{
_deviceLinkSystem.SaveLinks(args.Session.AttachedEntity, configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value, args.Links, targetSource);
UpdateLinkUiState(uid, configurator.DeviceLinkTarget.Value, configurator.ActiveDeviceLink.Value, targetSource);
}
} }
/// <summary> /// <summary>
@@ -307,11 +570,9 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
break; break;
case NetworkConfiguratorButtonKey.Copy: case NetworkConfiguratorButtonKey.Copy:
component.Devices = _deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value); component.Devices = _deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value);
UpdateUiState(uid, component); UpdateListUiState(uid, component);
return; return;
case NetworkConfiguratorButtonKey.Show: case NetworkConfiguratorButtonKey.Show:
// This should be done client-side.
// _deviceListSystem.ToggleVisualization(component.ActiveDeviceList.Value);
break; break;
} }
@@ -330,5 +591,11 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
_deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value) _deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value)
.Select(v => (v.Key, MetaData(v.Value).EntityName)).ToHashSet())); .Select(v => (v.Key, MetaData(v.Value).EntityName)).ToHashSet()));
} }
private void OnUiOpenAttempt(EntityUid uid, NetworkConfiguratorComponent configurator, ActivatableUIOpenAttemptEvent args)
{
if (configurator.LinkModeActive)
args.Cancel();
}
#endregion #endregion
} }

View File

@@ -1,3 +1,4 @@
using Content.Server.DeviceLinking.Events;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
@@ -8,7 +9,6 @@ using Content.Shared.Doors.Systems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Content.Shared.Wires; using Content.Shared.Wires;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System; using Content.Server.MachineLinking.System;
namespace Content.Server.Doors.Systems namespace Content.Server.Doors.Systems
@@ -44,7 +44,7 @@ namespace Content.Server.Doors.Systems
} }
} }
private void OnSignalReceived(EntityUid uid, AirlockComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, AirlockComponent component, ref SignalReceivedEvent args)
{ {
if (args.Port == component.AutoClosePort) if (args.Port == component.AutoClosePort)
{ {

View File

@@ -1,20 +1,19 @@
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Explosion.Components; using Content.Server.Explosion.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
namespace Content.Server.Explosion.EntitySystems namespace Content.Server.Explosion.EntitySystems
{ {
public sealed partial class TriggerSystem public sealed partial class TriggerSystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
private void InitializeSignal() private void InitializeSignal()
{ {
SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived); SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<TriggerOnSignalComponent,ComponentInit>(OnInit); SubscribeLocalEvent<TriggerOnSignalComponent,ComponentInit>(OnInit);
} }
private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, ref SignalReceivedEvent args)
{ {
if (args.Port != component.Port) if (args.Port != component.Port)
return; return;
@@ -23,7 +22,7 @@ namespace Content.Server.Explosion.EntitySystems
} }
private void OnInit(EntityUid uid, TriggerOnSignalComponent component, ComponentInit args) private void OnInit(EntityUid uid, TriggerOnSignalComponent component, ComponentInit args)
{ {
_signalSystem.EnsureReceiverPorts(uid, component.Port); _signalSystem.EnsureSinkPorts(uid, component.Port);
} }
} }
} }

View File

@@ -3,8 +3,6 @@ using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Systems; using Content.Server.DeviceNetwork.Systems;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Light.Components; using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Temperature.Components; using Content.Server.Temperature.Components;
using Content.Shared.Audio; using Content.Shared.Audio;
@@ -22,6 +20,8 @@ using Robust.Shared.Player;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Server.Emp; using Content.Server.Emp;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
namespace Content.Server.Light.EntitySystems namespace Content.Server.Light.EntitySystems
{ {
@@ -37,7 +37,7 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger= default!; [Dependency] private readonly IAdminLogManager _adminLogger= default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -69,7 +69,7 @@ namespace Content.Server.Light.EntitySystems
private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args) private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args)
{ {
light.LightBulbContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, LightBulbContainer); light.LightBulbContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, LightBulbContainer);
_signalSystem.EnsureReceiverPorts(uid, light.OnPort, light.OffPort, light.TogglePort); _signalSystem.EnsureSinkPorts(uid, light.OnPort, light.OffPort, light.TogglePort);
} }
private void OnMapInit(EntityUid uid, PoweredLightComponent light, MapInitEvent args) private void OnMapInit(EntityUid uid, PoweredLightComponent light, MapInitEvent args)
@@ -354,7 +354,7 @@ namespace Content.Server.Light.EntitySystems
_appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance); _appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance);
} }
private void OnSignalReceived(EntityUid uid, PoweredLightComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, PoweredLightComponent component, ref SignalReceivedEvent args)
{ {
if (args.Port == component.OffPort) if (args.Port == component.OffPort)
SetState(uid, false, component); SetState(uid, false, component);

View File

@@ -3,13 +3,13 @@ using Content.Server.MachineLinking.System;
namespace Content.Server.MachineLinking.Components namespace Content.Server.MachineLinking.Components
{ {
[DataDefinition] [DataDefinition]
public struct PortIdentifier public readonly struct PortIdentifier
{ {
[DataField("uid")] [DataField("uid")]
public EntityUid Uid; public readonly EntityUid Uid;
[DataField("port")] [DataField("port")]
public string Port; public readonly string Port;
public PortIdentifier(EntityUid uid, string port) public PortIdentifier(EntityUid uid, string port)
{ {

View File

@@ -1,14 +0,0 @@
namespace Content.Server.MachineLinking.Events
{
public sealed class SignalReceivedEvent : EntityEventArgs
{
public readonly string Port;
public readonly EntityUid? Trigger;
public SignalReceivedEvent(string port, EntityUid? trigger)
{
Port = port;
Trigger = trigger;
}
}
}

View File

@@ -1,18 +1,16 @@
using System.Linq; using System.Linq;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.DeviceLinking.Events;
using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Tools; using Content.Server.Tools;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.MachineLinking; using Content.Shared.MachineLinking;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.MachineLinking.Events;
using Content.Server.Database;
namespace Content.Server.MachineLinking.System namespace Content.Server.MachineLinking.System
{ {
@@ -140,7 +138,10 @@ namespace Content.Server.MachineLinking.System
return; return;
foreach (var receiver in receivers) foreach (var receiver in receivers)
RaiseLocalEvent(receiver.Uid, new SignalReceivedEvent(receiver.Port, uid), false); {
var eventArgs = new SignalReceivedEvent(receiver.Port, uid);
RaiseLocalEvent(receiver.Uid, ref eventArgs);
}
} }
private void OnTransmitterStartup(EntityUid uid, SignalTransmitterComponent transmitter, ComponentStartup args) private void OnTransmitterStartup(EntityUid uid, SignalTransmitterComponent transmitter, ComponentStartup args)

View File

@@ -1,30 +1,28 @@
using Content.Server.Climbing; using Content.Server.Climbing;
using Content.Server.Cloning; using Content.Server.Cloning;
using Content.Server.Medical.Components; using Content.Server.Medical.Components;
using Content.Server.Power.Components;
using Content.Shared.Destructible; using Content.Shared.Destructible;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop; using Content.Shared.DragDrop;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Server.Cloning.Components; using Content.Server.Cloning.Components;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.DeviceLinking.Systems;
using Content.Shared.DeviceLinking.Events;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Robust.Server.Containers; using Robust.Server.Containers;
using Robust.Server.GameObjects;
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent; // Hmm... using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent; // Hmm...
namespace Content.Server.Medical namespace Content.Server.Medical
{ {
public sealed class MedicalScannerSystem : EntitySystem public sealed class MedicalScannerSystem : EntitySystem
{ {
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!;
[Dependency] private readonly ClimbSystem _climbSystem = default!; [Dependency] private readonly ClimbSystem _climbSystem = default!;
[Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!; [Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!;
@@ -70,7 +68,7 @@ namespace Content.Server.Medical
{ {
base.Initialize(); base.Initialize();
scannerComponent.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, $"scanner-bodyContainer"); scannerComponent.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, $"scanner-bodyContainer");
_signalSystem.EnsureReceiverPorts(uid, MedicalScannerComponent.ScannerPort); _signalSystem.EnsureSinkPorts(uid, MedicalScannerComponent.ScannerPort);
} }
private void OnRelayMovement(EntityUid uid, MedicalScannerComponent scannerComponent, ref ContainerRelayMovementEntityEvent args) private void OnRelayMovement(EntityUid uid, MedicalScannerComponent scannerComponent, ref ContainerRelayMovementEntityEvent args)

View File

@@ -1,4 +1,6 @@
using Content.Server.MachineLinking.Events;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.MachineLinking.System; using Content.Server.MachineLinking.System;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Power.Components; using Content.Server.Power.Components;
@@ -9,7 +11,6 @@ using Content.Shared.Physics.Controllers;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
namespace Content.Server.Physics.Controllers; namespace Content.Server.Physics.Controllers;
@@ -17,8 +18,8 @@ namespace Content.Server.Physics.Controllers;
public sealed class ConveyorController : SharedConveyorController public sealed class ConveyorController : SharedConveyorController
{ {
[Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!; [Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -37,7 +38,7 @@ public sealed class ConveyorController : SharedConveyorController
private void OnInit(EntityUid uid, ConveyorComponent component, ComponentInit args) private void OnInit(EntityUid uid, ConveyorComponent component, ComponentInit args)
{ {
_signalSystem.EnsureReceiverPorts(uid, component.ReversePort, component.ForwardPort, component.OffPort); _signalSystem.EnsureSinkPorts(uid, component.ReversePort, component.ForwardPort, component.OffPort);
if (TryComp<PhysicsComponent>(uid, out var physics)) if (TryComp<PhysicsComponent>(uid, out var physics))
{ {
@@ -76,7 +77,7 @@ public sealed class ConveyorController : SharedConveyorController
_appearance.SetData(uid, ConveyorVisuals.State, component.Powered ? component.State : ConveyorState.Off); _appearance.SetData(uid, ConveyorVisuals.State, component.Powered ? component.State : ConveyorState.Off);
} }
private void OnSignalReceived(EntityUid uid, ConveyorComponent component, SignalReceivedEvent args) private void OnSignalReceived(EntityUid uid, ConveyorComponent component, ref SignalReceivedEvent args)
{ {
if (args.Port == component.OffPort) if (args.Port == component.OffPort)
SetState(uid, ConveyorState.Off, component); SetState(uid, ConveyorState.Off, component);

View File

@@ -1,7 +1,7 @@
using System.Linq; using System.Linq;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.DeviceLinking.Events;
using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.Paper; using Content.Server.Paper;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Research.Systems; using Content.Server.Research.Systems;
@@ -10,7 +10,7 @@ using Content.Server.Xenoarchaeology.Equipment.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.MachineLinking.Events; using Content.Shared.DeviceLinking.Events;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
using Content.Shared.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.Equipment;
@@ -168,10 +168,10 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
private void OnNewLink(EntityUid uid, AnalysisConsoleComponent component, NewLinkEvent args) private void OnNewLink(EntityUid uid, AnalysisConsoleComponent component, NewLinkEvent args)
{ {
if (!TryComp<ArtifactAnalyzerComponent>(args.Receiver, out var analyzer)) if (!TryComp<ArtifactAnalyzerComponent>(args.Sink, out var analyzer))
return; return;
component.AnalyzerEntity = args.Receiver; component.AnalyzerEntity = args.Sink;
analyzer.Console = uid; analyzer.Console = uid;
UpdateUserInterface(uid, component); UpdateUserInterface(uid, component);

View File

@@ -5,9 +5,9 @@ using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.DeviceLinking.Events;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.MachineLinking.Events;
using Content.Shared.StationRecords; using Content.Shared.StationRecords;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;

View File

@@ -0,0 +1,22 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.DeviceLinking;
[RegisterComponent]
[NetworkedComponent] // for interactions. Actual state isn't currently synced.
[Access(typeof(SharedDeviceLinkSystem))]
public sealed class DeviceLinkSinkComponent : Component
{
/// <summary>
/// The ports this sink has
/// </summary>
[DataField("ports", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<SinkPortPrototype>))]
public HashSet<string>? Ports;
/// <summary>
/// Used for removing a sink from all linked sources when it gets removed
/// </summary>
[DataField("links")]
public HashSet<EntityUid> LinkedSources = new();
}

View File

@@ -0,0 +1,35 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.DeviceLinking;
[RegisterComponent]
[NetworkedComponent] // for interactions. Actual state isn't currently synced.
[Access(typeof(SharedDeviceLinkSystem))]
public sealed class DeviceLinkSourceComponent : Component
{
/// <summary>
/// The ports the device link source sends signals from
/// </summary>
[DataField("ports", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<SourcePortPrototype>))]
public HashSet<string>? Ports;
/// <summary>
/// A list of sink uids that got linked for each port
/// </summary>
[DataField("registeredSinks")]
public Dictionary<string, HashSet<EntityUid>> Outputs = 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();
/// <summary>
/// Limits the range devices can be linked across.
/// </summary>
[DataField("range")]
[ViewVariables(VVAccess.ReadWrite)]
public float Range = 30f;
}

View File

@@ -0,0 +1,46 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.DeviceLinking;
/// <summary>
/// A prototype for a device port, for use with device linking.
/// </summary>
[Serializable, NetSerializable]
public abstract class DevicePortPrototype
{
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// Localization string for the port name. Displayed in the linking UI.
/// </summary>
[DataField("name", required:true)]
public string Name = default!;
/// <summary>
/// Localization string for a description of the ports functionality. Should either indicate when a source
/// port is fired, or what function a sink port serves. Displayed as a tooltip in the linking UI.
/// </summary>
[DataField("description", required: true)]
public string Description = default!;
}
[Prototype("sinkPort")]
[Serializable, NetSerializable]
public sealed class SinkPortPrototype : DevicePortPrototype, IPrototype
{
}
[Prototype("sourcePort")]
[Serializable, NetSerializable]
public sealed class SourcePortPrototype : DevicePortPrototype, IPrototype
{
/// <summary>
/// This is a set of sink ports that this source port will attempt to link to when using the
/// default-link functionality.
/// </summary>
[DataField("defaultLinks", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<SinkPortPrototype>))]
public readonly HashSet<string>? DefaultLinks;
}

View File

@@ -0,0 +1,19 @@
namespace Content.Shared.DeviceLinking.Events;
public sealed class LinkAttemptEvent : CancellableEntityEventArgs
{
public readonly EntityUid Source;
public readonly EntityUid Sink;
public readonly EntityUid? User;
public readonly string SourcePort;
public readonly string SinkPort;
public LinkAttemptEvent(EntityUid? user, EntityUid source, string sourcePort, EntityUid sink, string sinkPort)
{
User = user;
Source = source;
SourcePort = sourcePort;
Sink = sink;
SinkPort = sinkPort;
}
}

View File

@@ -0,0 +1,19 @@
namespace Content.Shared.DeviceLinking.Events;
public sealed class NewLinkEvent : EntityEventArgs
{
public readonly EntityUid Source;
public readonly EntityUid Sink;
public readonly EntityUid? User;
public readonly string SourcePort;
public readonly string SinkPort;
public NewLinkEvent(EntityUid? user, EntityUid source, string sourcePort, EntityUid sink, string sinkPort)
{
User = user;
Source = source;
SourcePort = sourcePort;
Sink = sink;
SinkPort = sinkPort;
}
}

View File

@@ -1,4 +1,4 @@
namespace Content.Server.MachineLinking.Events namespace Content.Shared.DeviceLinking.Events
{ {
public sealed class PortDisconnectedEvent : EntityEventArgs public sealed class PortDisconnectedEvent : EntityEventArgs
{ {

View File

@@ -0,0 +1,466 @@
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.DeviceLinking;
public abstract class SharedDeviceLinkSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
public const string InvokedPort = "link_port";
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<DeviceLinkSourceComponent, ComponentStartup>(OnSourceStartup);
SubscribeLocalEvent<DeviceLinkSinkComponent, ComponentStartup>(OnSinkStartup);
SubscribeLocalEvent<DeviceLinkSourceComponent, ComponentRemove>(OnSourceRemoved);
SubscribeLocalEvent<DeviceLinkSinkComponent, ComponentRemove>(OnSinkRemoved);
}
#region Link Validation
/// <summary>
/// Removes invalid links where the saved sink doesn't exist/have a sink component for example
/// </summary>
private void OnSourceStartup(EntityUid sourceUid, DeviceLinkSourceComponent sourceComponent, ComponentStartup args)
{
List<EntityUid> invalidSinks = new();
foreach (var sinkUid in sourceComponent.LinkedPorts.Keys)
{
if (!TryComp<DeviceLinkSinkComponent?>(sinkUid, out var sinkComponent))
{
invalidSinks.Add(sinkUid);
foreach (var savedSinks in sourceComponent.Outputs.Values)
{
savedSinks.Remove(sinkUid);
}
continue;
}
sinkComponent.LinkedSources.Add(sourceUid);
}
foreach (var invalidSink in invalidSinks)
{
sourceComponent.LinkedPorts.Remove(invalidSink);
}
}
/// <summary>
/// Same with <see cref="OnSourceStartup"/> but also checks that the saved ports are present on the sink
/// </summary>
private void OnSinkStartup(EntityUid sinkUid, DeviceLinkSinkComponent sinkComponent, ComponentStartup args)
{
List<EntityUid> invalidSources = new();
foreach (var sourceUid in sinkComponent.LinkedSources)
{
if (!TryComp<DeviceLinkSourceComponent>(sourceUid, out var sourceComponent))
{
invalidSources.Add(sourceUid);
continue;
}
if (!sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var linkedPorts))
{
foreach (var savedSinks in sourceComponent.Outputs.Values)
{
savedSinks.Remove(sinkUid);
}
continue;
}
if (sinkComponent.Ports == null)
continue;
List<(string, string)> invalidLinks = new();
foreach (var link in linkedPorts)
{
if (!sinkComponent.Ports.Contains(link.sink) || !(sourceComponent.Outputs.GetValueOrDefault(link.source)?.Contains(sinkUid) ?? false))
invalidLinks.Add(link);
}
foreach (var invalidLink in invalidLinks)
{
linkedPorts.Remove(invalidLink);
sourceComponent.Outputs.GetValueOrDefault(invalidLink.Item1)?.Remove(sinkUid);
}
}
foreach (var invalidSource in invalidSources)
{
sinkComponent.LinkedSources.Remove(invalidSource);
}
}
#endregion
/// <summary>
/// Ensures that its links get deleted when a source gets removed
/// </summary>
private void OnSourceRemoved(EntityUid uid, DeviceLinkSourceComponent component, ComponentRemove args)
{
foreach (var sinkUid in component.LinkedPorts.Keys)
{
RemoveSinkFromSource(uid, sinkUid, component);
}
}
/// <summary>
/// Ensures that its links get deleted when a sink gets removed
/// </summary>
private void OnSinkRemoved(EntityUid sinkUid, DeviceLinkSinkComponent sinkComponent, ComponentRemove args)
{
foreach (var linkedSource in sinkComponent.LinkedSources)
{
RemoveSinkFromSource(linkedSource, sinkUid, null, sinkComponent);
}
}
#region Ports
/// <summary>
/// Convenience function to add several ports to an entity
/// </summary>
public void EnsureSourcePorts(EntityUid uid, params string[] ports)
{
var comp = EnsureComp<DeviceLinkSourceComponent>(uid);
comp.Ports ??= new HashSet<string>();
foreach (var port in ports)
{
comp.Ports?.Add(port);
}
}
/// <summary>
/// Convenience function to add several ports to an entity.
/// </summary>
public void EnsureSinkPorts(EntityUid uid, params string[] ports)
{
var comp = EnsureComp<DeviceLinkSinkComponent>(uid);
comp.Ports ??= new HashSet<string>();
foreach (var port in ports)
{
comp.Ports?.Add(port);
}
}
/// <summary>
/// Retrieves the available ports from a source
/// </summary>
/// <returns>A list of source port prototypes</returns>
public List<SourcePortPrototype> GetSourcePorts(EntityUid sourceUid, DeviceLinkSourceComponent? sourceComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent) || sourceComponent.Ports == null)
return new List<SourcePortPrototype>();
var sourcePorts = new List<SourcePortPrototype>();
foreach (var port in sourceComponent.Ports)
{
sourcePorts.Add(_prototypeManager.Index<SourcePortPrototype>(port));
}
return sourcePorts;
}
/// <summary>
/// Retrieves the available ports from a sink
/// </summary>
/// <returns>A list of sink port prototypes</returns>
public List<SinkPortPrototype> GetSinkPorts(EntityUid sinkUid, DeviceLinkSinkComponent? sinkComponent = null)
{
if (!Resolve(sinkUid, ref sinkComponent) || sinkComponent.Ports == null)
return new List<SinkPortPrototype>();
var sinkPorts = new List<SinkPortPrototype>();
foreach (var port in sinkComponent.Ports)
{
sinkPorts.Add(_prototypeManager.Index<SinkPortPrototype>(port));
}
return sinkPorts;
}
/// <summary>
/// Convenience function to retrieve the name of a port prototype
/// </summary>
public string PortName<TPort>(string port) where TPort : DevicePortPrototype, IPrototype
{
if (!_prototypeManager.TryIndex<TPort>(port, out var proto))
return port;
return Loc.GetString(proto.Name);
}
#endregion
#region Links
/// <summary>
/// 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)
{
if (!Resolve(sourceUid, ref sourceComponent) || !sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var links))
return new HashSet<(string source, string sink)>();
return links;
}
/// <summary>
/// Returns the default links for the given list of source port prototypes
/// </summary>
/// <param name="sources">The list of source port prototypes to get the default links for</param>
/// <returns>A list of sink and source port ids</returns>
public List<(string source, string sink)> GetDefaults(List<SourcePortPrototype> sources)
{
var defaults = new List<(string, string)>();
foreach (var source in sources)
{
if (source.DefaultLinks == null)
return new List<(string, string)>();
foreach (var defaultLink in source.DefaultLinks)
{
defaults.Add((source.ID, defaultLink));
}
}
return defaults;
}
/// <summary>
/// Links the given source and sink by their default links
/// </summary>
/// <param name="userId">Optinal user uid for displaying popups</param>
/// <param name="sourceUid">The source uid</param>
/// <param name="sinkUid">The sink uid</param>
/// <param name="sourceComponent"></param>
/// <param name="sinkComponent"></param>
public void LinkDefaults(
EntityUid? userId,
EntityUid sourceUid,
EntityUid sinkUid,
DeviceLinkSourceComponent? sourceComponent = null,
DeviceLinkSinkComponent? sinkComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent) || !Resolve(sinkUid, ref sinkComponent))
return;
var sourcePorts = GetSourcePorts(sourceUid, sourceComponent);
var defaults = GetDefaults(sourcePorts);
SaveLinks(userId, sourceUid, sinkUid, defaults, sourceComponent, sinkComponent);
if (userId != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linking-verb-success", ("machine", sourceUid)), userId.Value);
}
/// <summary>
/// Saves multiple links between a source and a sink device.
/// Ignores links where either the source or sink port aren't present
/// </summary>
/// <param name="userId">Optinal user uid for displaying popups</param>
/// <param name="sourceUid">The source uid</param>
/// <param name="sinkUid">The sink uid</param>
/// <param name="links">List of source and sink ids to link</param>
/// <param name="sourceComponent"></param>
/// <param name="sinkComponent"></param>
public void SaveLinks(
EntityUid? userId,
EntityUid sourceUid,
EntityUid sinkUid,
List<(string source, string sink)> links,
DeviceLinkSourceComponent? sourceComponent = null,
DeviceLinkSinkComponent? sinkComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent) || !Resolve(sinkUid, ref sinkComponent))
return;
if (sourceComponent.Ports == null || sinkComponent.Ports == null)
return;
if (!InRange(sourceUid, sinkUid, sourceComponent.Range))
{
if (userId != null)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-out-of-range"), userId.Value);
return;
}
RemoveSinkFromSource(sourceUid, sinkUid, sourceComponent);
foreach (var (source, sink) in links)
{
if (!sourceComponent.Ports.Contains(source) || !sinkComponent.Ports.Contains(sink))
continue;
if (!CanLink(userId, sourceUid, sinkUid, source, sink, false, sourceComponent))
continue;
sourceComponent.Outputs.GetOrNew(source).Add(sinkUid);
sourceComponent.LinkedPorts.GetOrNew(sinkUid).Add((source, sink));
SendNewLinkEvent(userId, sourceUid, source, sinkUid, sink);
}
if (links.Count > 0)
sinkComponent.LinkedSources.Add(sourceUid);
}
/// <summary>
/// Removes all links between a source and a sink
/// </summary>
public void RemoveSinkFromSource(
EntityUid sourceUid,
EntityUid sinkUid,
DeviceLinkSourceComponent? sourceComponent = null,
DeviceLinkSinkComponent? sinkComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent, false) || !Resolve(sinkUid, ref sinkComponent, false))
return;
if (sourceComponent.LinkedPorts.TryGetValue(sinkUid, out var ports))
{
foreach (var (sourcePort, sinkPort) in ports)
{
RaiseLocalEvent(sourceUid, new PortDisconnectedEvent(sourcePort));
RaiseLocalEvent(sinkUid, new PortDisconnectedEvent(sinkPort));
}
}
sinkComponent.LinkedSources.Remove(sourceUid);
sourceComponent.LinkedPorts.Remove(sinkUid);
var outputLists = sourceComponent.Outputs.Values;
foreach (var outputList in outputLists)
{
outputList.Remove(sinkUid);
}
}
/// <summary>
/// Adds or removes a link depending on if it's already present
/// </summary>
/// <returns>True if the link was successfully added or removed</returns>
public bool ToggleLink(
EntityUid? userId,
EntityUid sourceUid,
EntityUid sinkUid,
string source,
string sink,
DeviceLinkSourceComponent? sourceComponent = null,
DeviceLinkSinkComponent? sinkComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent) || !Resolve(sinkUid, ref sinkComponent))
return false;
if (sourceComponent.Ports == null || sinkComponent.Ports == null)
return false;
var outputs = sourceComponent.Outputs.GetOrNew(source);
var linkedPorts = sourceComponent.LinkedPorts.GetOrNew(sinkUid);
if (linkedPorts.Contains((source, sink)))
{
RaiseLocalEvent(sourceUid, new PortDisconnectedEvent(source));
RaiseLocalEvent(sinkUid, new PortDisconnectedEvent(sink));
outputs.Remove(sinkUid);
linkedPorts.Remove((source, sink));
if (linkedPorts.Count != 0)
return true;
sourceComponent.LinkedPorts.Remove(sinkUid);
sinkComponent.LinkedSources.Remove(sourceUid);
CreateLinkPopup(userId, sourceUid, source, sinkUid, sink, true);
}
else
{
if (!sourceComponent.Ports.Contains(source) || !sinkComponent.Ports.Contains(sink))
return false;
if (!CanLink(userId, sourceUid, sinkUid, source, sink, true, sourceComponent))
return false;
outputs.Add(sinkUid);
linkedPorts.Add((source, sink));
sinkComponent.LinkedSources.Add(sourceUid);
SendNewLinkEvent(userId, sourceUid, source, sinkUid, sink);
CreateLinkPopup(userId, sourceUid, source, sinkUid, sink, false);
}
return true;
}
/// <summary>
/// Checks if a source and a sink can be linked by allowing other systems to veto the link
/// and by optionally checking if they are in range of each other
/// </summary>
/// <returns></returns>
private bool CanLink(
EntityUid? userId,
EntityUid sourceUid,
EntityUid sinkUid,
string source,
string sink,
bool checkRange = true,
DeviceLinkSourceComponent? sourceComponent = null)
{
if (!Resolve(sourceUid, ref sourceComponent))
return false;
if (checkRange && !InRange(sourceUid, sinkUid, sourceComponent.Range))
{
if (userId.HasValue)
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-out-of-range"), userId.Value);
return false;
}
var linkAttemptEvent = new LinkAttemptEvent(userId, sourceUid, source, sinkUid, sink);
RaiseLocalEvent(sourceUid, linkAttemptEvent, true);
if (linkAttemptEvent.Cancelled && userId.HasValue)
{
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-connection-refused", ("machine", source)), userId.Value);
return false;
}
RaiseLocalEvent(sinkUid, linkAttemptEvent, true);
if (linkAttemptEvent.Cancelled && userId.HasValue)
{
_popupSystem.PopupCursor(Loc.GetString("signal-linker-component-connection-refused", ("machine", source)), userId.Value);
return false;
}
return !linkAttemptEvent.Cancelled;
}
private bool InRange(EntityUid sourceUid, EntityUid sinkUid, float range)
{
return Transform(sourceUid).MapPosition.InRange(Transform(sinkUid).MapPosition, range);
}
private void SendNewLinkEvent(EntityUid? user, EntityUid sourceUid, string source, EntityUid sinkUid, string sink)
{
var newLinkEvent = new NewLinkEvent(user, sourceUid, source, sinkUid, sink);
RaiseLocalEvent(sourceUid, newLinkEvent);
RaiseLocalEvent(sinkUid, newLinkEvent);
}
private void CreateLinkPopup(EntityUid? userId, EntityUid sourceUid, string source, EntityUid sinkUid, string sink, bool removed)
{
if (!userId.HasValue)
return;
var locString = removed ? "signal-linker-component-unlinked-port" : "signal-linker-component-linked-port";
_popupSystem.PopupCursor(Loc.GetString(locString, ("machine1", sourceUid), ("port1", PortName<SourcePortPrototype>(source)),
("machine2", sinkUid), ("port2", PortName<SinkPortPrototype>(sink))), userId.Value, PopupType.Medium);
}
#endregion
}

View File

@@ -1,6 +1,6 @@
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.MachineLinking namespace Content.Shared.DeviceLinking
{ {
[Serializable, NetSerializable] [Serializable, NetSerializable]
public enum TwoWayLeverVisuals : byte public enum TwoWayLeverVisuals : byte

View File

@@ -1,7 +1,7 @@
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork.Components;
[RegisterComponent] [RegisterComponent]
[NetworkedComponent] [NetworkedComponent]

View File

@@ -1,14 +1,24 @@
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork.Systems;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork.Components;
[RegisterComponent] [RegisterComponent]
[NetworkedComponent] [NetworkedComponent]
[Access(typeof(SharedNetworkConfiguratorSystem))] [Access(typeof(SharedNetworkConfiguratorSystem))]
public sealed class NetworkConfiguratorComponent : Component public sealed class NetworkConfiguratorComponent : Component
{ {
/// <summary>
/// Determines whether the configurator is in linking mode or list mode
/// </summary>
[DataField("linkModeActive")]
[ViewVariables(VVAccess.ReadWrite)]
public bool LinkModeActive = false;
/// <summary> /// <summary>
/// The entity containing a <see cref="DeviceListComponent"/> this configurator is currently interacting with /// The entity containing a <see cref="DeviceListComponent"/> this configurator is currently interacting with
/// </summary> /// </summary>
@@ -16,22 +26,48 @@ public sealed class NetworkConfiguratorComponent : Component
public EntityUid? ActiveDeviceList = null; public EntityUid? ActiveDeviceList = null;
/// <summary> /// <summary>
/// The list of devices stored in the configurator- /// The entity containing a <see cref="DeviceLinkSourceComponent"/> or <see cref="DeviceLinkSinkComponent"/> this configurator is currently interacting with.<br/>
/// If this is set the configurator is in linking mode.
/// </summary>
[DataField("activeDeviceLink")]
public EntityUid? ActiveDeviceLink = null;
/// <summary>
/// The target device this configurator is currently linking with the <see cref="ActiveDeviceLink"/>
/// </summary>
[DataField("deviceLinkTarget")]
public EntityUid? DeviceLinkTarget = null;
/// <summary>
/// The list of devices stored in the configurator
/// </summary> /// </summary>
[DataField("devices")] [DataField("devices")]
public Dictionary<string, EntityUid> Devices = new(); public Dictionary<string, EntityUid> Devices = new();
[DataField("useDelay")]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan UseDelay = TimeSpan.FromSeconds(0.5);
[DataField("lastUseAttempt", customTypeSerializer:typeof(TimeOffsetSerializer))]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan LastUseAttempt;
[DataField("soundNoAccess")] [DataField("soundNoAccess")]
public SoundSpecifier SoundNoAccess = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg"); public SoundSpecifier SoundNoAccess = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
[DataField("soundSwitchMode")]
public SoundSpecifier SoundSwitchMode = new SoundPathSpecifier("/Audio/Machines/beep.ogg");
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
public sealed class NetworkConfiguratorComponentState : ComponentState public sealed class NetworkConfiguratorComponentState : ComponentState
{ {
public readonly EntityUid? ActiveDeviceList; public readonly EntityUid? ActiveDeviceList;
public readonly bool LinkModeActive;
public NetworkConfiguratorComponentState(EntityUid? activeDeviceList) public NetworkConfiguratorComponentState(EntityUid? activeDeviceList, bool linkModeActive)
{ {
ActiveDeviceList = activeDeviceList; ActiveDeviceList = activeDeviceList;
LinkModeActive = linkModeActive;
} }
} }

View File

@@ -6,7 +6,8 @@ namespace Content.Shared.DeviceNetwork;
public enum NetworkConfiguratorUiKey public enum NetworkConfiguratorUiKey
{ {
List, List,
Configure Configure,
Link
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
@@ -52,3 +53,33 @@ public sealed class NetworkConfiguratorButtonPressedMessage : BoundUserInterface
ButtonKey = buttonKey; ButtonKey = buttonKey;
} }
} }
[Serializable, NetSerializable]
public sealed class NetworkConfiguratorClearLinksMessage : BoundUserInterfaceMessage
{
}
[Serializable, NetSerializable]
public sealed class NetworkConfiguratorToggleLinkMessage : BoundUserInterfaceMessage
{
public readonly string Source;
public readonly string Sink;
public NetworkConfiguratorToggleLinkMessage(string source, string sink)
{
Source = source;
Sink = sink;
}
}
[Serializable, NetSerializable]
public sealed class NetworkConfiguratorLinksSaveMessage : BoundUserInterfaceMessage
{
public readonly List<(string source, string sink)> Links;
public NetworkConfiguratorLinksSaveMessage(List<(string source, string sink)> links)
{
Links = links;
}
}

View File

@@ -1,4 +1,5 @@
using Robust.Shared.Serialization; using Content.Shared.DeviceLinking;
using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork;
@@ -23,3 +24,24 @@ public sealed class DeviceListUserInterfaceState : BoundUserInterfaceState
DeviceList = deviceList; DeviceList = deviceList;
} }
} }
[Serializable, NetSerializable]
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 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)
{
Links = links;
SourceAddress = sourceAddress;
SinkAddress = sinkAddress;
Defaults = defaults;
Sources = sources;
Sinks = sinks;
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Shared.DeviceNetwork.Components;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork;

View File

@@ -1,8 +1,9 @@
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.DeviceNetwork.Components;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork; namespace Content.Shared.DeviceNetwork.Systems;
public abstract class SharedNetworkConfiguratorSystem : EntitySystem public abstract class SharedNetworkConfiguratorSystem : EntitySystem
{ {
@@ -17,7 +18,7 @@ public abstract class SharedNetworkConfiguratorSystem : EntitySystem
private void GetNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp, private void GetNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp,
ref ComponentGetState args) ref ComponentGetState args)
{ {
args.State = new NetworkConfiguratorComponentState(comp.ActiveDeviceList); args.State = new NetworkConfiguratorComponentState(comp.ActiveDeviceList, comp.LinkModeActive);
} }
private void HandleNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp, private void HandleNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp,
@@ -29,9 +30,22 @@ public abstract class SharedNetworkConfiguratorSystem : EntitySystem
} }
comp.ActiveDeviceList = state.ActiveDeviceList; comp.ActiveDeviceList = state.ActiveDeviceList;
comp.LinkModeActive = state.LinkModeActive;
} }
} }
public sealed class ClearAllOverlaysEvent : InstantActionEvent public sealed class ClearAllOverlaysEvent : InstantActionEvent
{ {
} }
[Serializable, NetSerializable]
public enum NetworkConfiguratorVisuals
{
Mode
}
[Serializable, NetSerializable]
public enum NetworkConfiguratorLayers
{
ModeLight
}

View File

@@ -1,20 +0,0 @@
namespace Content.Shared.MachineLinking.Events
{
public sealed class LinkAttemptEvent : CancellableEntityEventArgs
{
public readonly EntityUid Transmitter;
public readonly EntityUid Receiver;
public readonly EntityUid? User;
public readonly string TransmitterPort;
public readonly string ReceiverPort;
public LinkAttemptEvent(EntityUid? user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
{
User = user;
Transmitter = transmitter;
TransmitterPort = transmitterPort;
Receiver = receiver;
ReceiverPort = receiverPort;
}
}
}

View File

@@ -1,20 +0,0 @@
namespace Content.Shared.MachineLinking.Events
{
public sealed class NewLinkEvent : EntityEventArgs
{
public readonly EntityUid Transmitter;
public readonly EntityUid Receiver;
public readonly EntityUid? User;
public readonly string TransmitterPort;
public readonly string ReceiverPort;
public NewLinkEvent(EntityUid? user, EntityUid transmitter, string transmitterPort, EntityUid receiver, string receiverPort)
{
User = user;
Transmitter = transmitter;
TransmitterPort = transmitterPort;
Receiver = receiver;
ReceiverPort = receiverPort;
}
}
}

View File

@@ -26,4 +26,4 @@
- files: ["vessel_warning.ogg"] - files: ["vessel_warning.ogg"]
license: "CC-BY-4.0" license: "CC-BY-4.0"
copyright: "Created by AUDACITIER (freesound), converted to MONO and .ogg and edited by EmoGarbage404 (github)." copyright: "Created by AUDACITIER (freesound), converted to MONO and .ogg and edited by EmoGarbage404 (github)."
source: "https://freesound.org/people/AUDACITIER/sounds/629196/" source: "https://freesound.org/people/AUDACITIER/sounds/629196/"

View File

@@ -6,6 +6,7 @@ device-frequency-prototype-name-lights = Smart Lights
device-frequency-prototype-name-mailing-units = Mailing Units device-frequency-prototype-name-mailing-units = Mailing Units
device-frequency-prototype-name-pdas = PDAs device-frequency-prototype-name-pdas = PDAs
device-frequency-prototype-name-fax = Fax device-frequency-prototype-name-fax = Fax
device-frequency-prototype-name-basic-device = Basic Devices
## camera frequencies ## camera frequencies
device-frequency-prototype-name-surveillance-camera-test = Subnet Test device-frequency-prototype-name-surveillance-camera-test = Subnet Test

View File

@@ -6,12 +6,23 @@ network-configurator-too-many-devices = Too many devices stored on this device!
network-configurator-update-ok = Device storage updated. network-configurator-update-ok = Device storage updated.
network-configurator-device-already-saved = network device: {$device} is already saved. network-configurator-device-already-saved = network device: {$device} is already saved.
network-configurator-device-access-denied = Access denied! network-configurator-device-access-denied = Access denied!
network-configurator-link-mode-started = Started linking device: {$device}
network-configurator-link-mode-stopped = Stopped linking.
network-configurator-mode-link = Link
network-configurator-mode-list = List
network-configurator-switched-mode = Switched mode to: {$mode}
# Verbs # Verbs
network-configurator-save-device = Save device network-configurator-save-device = Save device
network-configurator-configure = Configure network-configurator-configure = Configure
network-configurator-switch-mode = Switch mode
network-configurator-link-defaults = Link defaults
network-configurator-start-link = Start link
network-configurator-link = Link
# ui # ui
network-configurator-title-saved-devices = Saved Devices
network-configurator-title-device-configuration = Device Configuration
network-configurator-ui-clear-button = Clear network-configurator-ui-clear-button = Clear
network-configurator-ui-count-label = {$count} Devices network-configurator-ui-count-label = {$count} Devices
network-configurator-clear-network-link-overlays = Clear network link overlays network-configurator-clear-network-link-overlays = Clear network link overlays
@@ -24,3 +35,13 @@ network-configurator-tooltip-edit = Edit targets device list
network-configurator-tooltip-clear = Clear targets device list network-configurator-tooltip-clear = Clear targets device list
network-configurator-tooltip-copy = Copy targets device list to multitool network-configurator-tooltip-copy = Copy targets device list to multitool
network-configurator-tooltip-show = Show a holographic visualization of targets device list network-configurator-tooltip-show = Show a holographic visualization of targets device list
# examine
network-configurator-examine-mode-link = [color=red]Link[/color]
network-configurator-examine-mode-list = [color=green]List[/color]
network-configurator-examine-current-mode = Current mode: {$mode}
network-configurator-examine-switch-modes = Press {$key} to switch modes
# item status
network-configurator-item-status-label = Current mode: {$mode}
{$keybinding} to switch mode

View File

@@ -4,6 +4,8 @@ guide-entry-atmospherics = Atmospherics
guide-entry-botany = Botany guide-entry-botany = Botany
guide-entry-fires = Fires & Space guide-entry-fires = Fires & Space
guide-entry-shuttle-craft = Shuttle-craft guide-entry-shuttle-craft = Shuttle-craft
guide-entry-networking = Networking
guide-entry-network-configurator = Network Configurator
guide-entry-power = Power guide-entry-power = Power
guide-entry-ame = Antimatter Engine (AME) guide-entry-ame = Antimatter Engine (AME)
guide-entry-singularity = Singularity guide-entry-singularity = Singularity

View File

@@ -1,3 +1,5 @@
signal-port-selector-menu-title = Port Selector signal-port-selector-menu-title = Port Selector
signal-port-selector-menu-clear = Clear signal-port-selector-menu-clear = Clear
signal-port-selector-menu-link-defaults = Link defaults signal-port-selector-menu-link-defaults = Link defaults
signal-port-selector-help = Select the ports you want to link up
signal-port-selector-menu-done = Done

View File

@@ -1,3 +1,6 @@
signal-port-name-toggle = Autoclose
signal-port-description-toggle = Toggles whether the device should automatically close.
signal-port-name-toggle = Toggle signal-port-name-toggle = Toggle
signal-port-description-toggle = Toggles the state of a device. signal-port-description-toggle = Toggles the state of a device.

View File

@@ -11,6 +11,7 @@
- id: Wirecutter - id: Wirecutter
- id: Welder - id: Welder
- id: Multitool - id: Multitool
- id: NetworkConfigurator
- type: entity - type: entity
id: ClothingBeltChiefEngineerFilled id: ClothingBeltChiefEngineerFilled
@@ -24,6 +25,7 @@
- id: WelderExperimental - id: WelderExperimental
- id: Multitool - id: Multitool
- id: CableApcStack - id: CableApcStack
- id: NetworkConfigurator
- type: entity - type: entity
id: ClothingBeltSecurityFilled id: ClothingBeltSecurityFilled

View File

@@ -86,3 +86,8 @@
id: Fax id: Fax
name: device-frequency-prototype-name-fax name: device-frequency-prototype-name-fax
frequency: 2640 frequency: 2640
- type: deviceFrequency
id: BasicDevice
name: device-frequency-prototype-name-basic-device
frequency: 1280

View File

@@ -0,0 +1,74 @@
- type: sinkPort
id: AutoClose
name: signal-port-name-autoclose
description: signal-port-description-autoclose
- type: sinkPort
id: Toggle
name: signal-port-name-toggle
description: signal-port-description-toggle
- type: sinkPort
id: On
name: signal-port-name-on-receiver
description: signal-port-description-on-receiver
- type: sinkPort
id: Off
name: signal-port-name-off-receiver
description: signal-port-description-off-receiver
- type: sinkPort
id: Forward
name: signal-port-name-forward
description: signal-port-description-forward
- type: sinkPort
id: Reverse
name: signal-port-name-reverse
description: signal-port-description-reverse
- type: sinkPort
id: Open
name: signal-port-name-open
description: signal-port-description-open
- type: sinkPort
id: Close
name: signal-port-name-close
description: signal-port-description-close
- type: sinkPort
id: Trigger
name: signal-port-name-trigger
description: signal-port-description-trigger
- type: sinkPort
id: OrderReceiver
name: signal-port-name-order-receiver
description: signal-port-description-order-receiver
- type: sinkPort
id: Pressurize
name: signal-port-name-pressurize
description: signal-port-description-pressurize
- type: sinkPort
id: Depressurize
name: signal-port-name-depressurize
description: signal-port-description-depressurize
- type: sinkPort
id: CloningPodReceiver
name: signal-port-name-pod-receiver
description: signal-port-description-pod-receiver
- type: sinkPort
id: MedicalScannerReceiver
name: signal-port-name-med-scanner-receiver
description: signal-port-description-med-scanner-receiver
- type: sinkPort
id: ArtifactAnalyzerReceiver
name: signal-port-name-artifact-analyzer-receiver
description: signal-port-description-artifact-analyzer-receiver

View File

@@ -0,0 +1,57 @@
- type: sourcePort
id: Pressed
name: signal-port-name-pressed
description: signal-port-description-pressed
defaultLinks: [ Toggle, Trigger ]
- type: sourcePort
id: On
name: signal-port-name-on-transmitter
description: signal-port-description-on-transmitter
defaultLinks: [ On, Open, Forward, Trigger ]
- type: sourcePort
id: Off
name: signal-port-name-off-transmitter
description: signal-port-description-off-transmitter
defaultLinks: [ Off, Close ]
- type: sourcePort
id: Left
name: signal-port-name-left
description: signal-port-description-left
defaultLinks: [ On, Open, Forward, Trigger ]
- type: sourcePort
id: Right
name: signal-port-name-right
description: signal-port-description-right
defaultLinks: [ On, Open, Reverse, Trigger ]
- type: sourcePort
id: Middle
name: signal-port-name-middle
description: signal-port-description-middle
defaultLinks: [ Off, Close ]
- type: sourcePort
id: OrderSender
name: signal-port-name-order-sender
description: signal-port-description-order-sender
defaultLinks: [ OrderReceiver ]
- type: sourcePort
id: CloningPodSender
name: signal-port-name-pod-receiver
description: signal-port-description-pod-sender
- type: sourcePort
id: MedicalScannerSender
name: signal-port-name-med-scanner-sender
description: signal-port-description-med-scanner-sender
- type: sourcePort
id: ArtifactAnalyzerSender
name: signal-port-name-artifact-analyzer-sender
description: signal-port-description-artifact-analyzer-sender
defaultLinks: [ ArtifactAnalyzerReceiver ]

View File

@@ -11,6 +11,7 @@
- type: Clothing - type: Clothing
sprite: Clothing/Belt/utility.rsi sprite: Clothing/Belt/utility.rsi
- type: Storage - type: Storage
capacity: 45
# TODO: Fill this out more. # TODO: Fill this out more.
whitelist: whitelist:
tags: tags:
@@ -26,10 +27,11 @@
- CigPack - CigPack
- Radio - Radio
- HolofanProjector - HolofanProjector
- Multitool
- AppraisalTool - AppraisalTool
components: components:
- AirlockPainter - AirlockPainter
- SignalLinker - NetworkConfigurator
- RCD - RCD
- RCDAmmo - RCDAmmo
- Welder - Welder
@@ -63,8 +65,8 @@
- Wrench - Wrench
multitool: multitool:
whitelist: whitelist:
components: tags:
- SignalLinker - Multitool
sprite: Clothing/Belt/belt_overlay.rsi sprite: Clothing/Belt/belt_overlay.rsi
- type: Appearance - type: Appearance
@@ -79,7 +81,7 @@
- type: Clothing - type: Clothing
sprite: Clothing/Belt/ce.rsi sprite: Clothing/Belt/ce.rsi
- type: Storage - type: Storage
capacity: 100 capacity: 105
# TODO: Fill this out more. # TODO: Fill this out more.
whitelist: whitelist:
tags: tags:
@@ -96,10 +98,11 @@
- CigPack - CigPack
- Radio - Radio
- HolofanProjector - HolofanProjector
- Multitool
- AppraisalTool - AppraisalTool
components: components:
- AirlockPainter - AirlockPainter
- SignalLinker - NetworkConfigurator
- RCD - RCD
- RCDAmmo - RCDAmmo
- Welder - Welder
@@ -135,8 +138,8 @@
- Screwdriver - Screwdriver
multitool: multitool:
whitelist: whitelist:
components: tags:
- SignalLinker - Multitool
wrench: wrench:
whitelist: whitelist:
tags: tags:

View File

@@ -13,9 +13,9 @@
- type: UseDelay - type: UseDelay
- type: StaticPrice - type: StaticPrice
price: 40 price: 40
- type: SignalTransmitter - type: DeviceLinkSource
outputs: ports:
Pressed: [] - Pressed
- type: Tag - type: Tag
tags: tags:
- Payload - Payload

View File

@@ -42,7 +42,12 @@
- type: PayloadTrigger - type: PayloadTrigger
components: components:
- type: TriggerOnSignal - type: TriggerOnSignal
- type: SignalReceiver - type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice
- type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSink
- type: StaticPrice - type: StaticPrice
price: 40 price: 40

View File

@@ -108,7 +108,9 @@
- type: Tool - type: Tool
qualities: qualities:
- Pulsing - Pulsing
- type: SignalLinker - type: Tag
tags:
- Multitool
- type: entity - type: entity
name: cowelding tool name: cowelding tool

View File

@@ -196,21 +196,9 @@
- type: Tool - type: Tool
qualities: qualities:
- Pulsing - Pulsing
- type: SignalLinker
- type: NetworkConfigurator
- type: ActivatableUI
key: enum.NetworkConfiguratorUiKey.List
inHandsOnly: true
- type: UserInterface
interfaces:
- key: enum.SignalLinkerUiKey.Key
type: SignalPortSelectorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.List
type: NetworkConfiguratorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.Configure
type: NetworkConfiguratorBoundUserInterface
- type: Tag - type: Tag
tags: tags:
- Multitool
- DroneUsable - DroneUsable
- type: PhysicalComposition - type: PhysicalComposition
materialComposition: materialComposition:
@@ -219,6 +207,54 @@
- type: StaticPrice - type: StaticPrice
price: 60 price: 60
- type: entity
name: network configurator
parent: BaseItem
id: NetworkConfigurator
description: A tool for linking devices together. Has two modes, a list mode for mass linking devices and a linking mode for advanced device linking.
components:
- type: EmitSoundOnLand
sound:
path: /Audio/Items/multitool_drop.ogg
- type: Sprite
sprite: Objects/Tools/network_configurator.rsi
layers:
- state: icon
- state: mode-list
map: ["enum.NetworkConfiguratorLayers.ModeLight"]
shader: unshaded
- type: Item
size: 5
- type: Clothing
sprite: Objects/Tools/network_configurator.rsi
quickEquip: false
slots:
- Belt
- type: Appearance
- type: GenericVisualizer
visuals:
enum.NetworkConfiguratorVisuals.Mode:
enum.NetworkConfiguratorLayers.ModeLight:
True: { state: mode-link }
False: { state: mode-list }
- type: NetworkConfigurator
- type: ActivatableUI
key: enum.NetworkConfiguratorUiKey.List
inHandsOnly: true
- type: UserInterface
interfaces:
- key: enum.NetworkConfiguratorUiKey.List
type: NetworkConfiguratorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.Configure
type: NetworkConfiguratorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.Link
type: NetworkConfiguratorBoundUserInterface
- type: Tag
tags:
- DroneUsable
- type: StaticPrice
price: 60
- type: entity - type: entity
name: power drill name: power drill
parent: BaseItem parent: BaseItem
@@ -371,13 +407,10 @@
- type: Item - type: Item
sprite: Objects/Tools/omnitool.rsi sprite: Objects/Tools/omnitool.rsi
size: 20 size: 20
- type: Tag
tags:
- Multitool
- type: TilePrying - type: TilePrying
- type: SignalLinker
requiredQuality: Pulsing
- type: UserInterface
interfaces:
- key: enum.SignalLinkerUiKey.Key
type: SignalPortSelectorBoundUserInterface
- type: Tool - type: Tool
qualities: qualities:
- Screwing - Screwing

View File

@@ -18,9 +18,9 @@
startOnStick: true startOnStick: true
canToggleStartOnStick: true canToggleStartOnStick: true
- type: TriggerOnSignal - type: TriggerOnSignal
- type: SignalReceiver - type: DeviceLinkSink
inputs: ports:
Trigger: [] - Trigger
- type: Sticky - type: Sticky
stickDelay: 5 stickDelay: 5
unstickDelay: 5 unstickDelay: 5

View File

@@ -78,12 +78,17 @@
BoardName: "Airlock Control" BoardName: "Airlock Control"
LayoutId: Airlock LayoutId: Airlock
- type: DoorSignalControl - type: DoorSignalControl
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
Open: [] receiveFrequencyId: BasicDevice
Close: [] - type: WirelessNetworkConnection
Toggle: [] range: 200
AutoClose: [] - type: DeviceLinkSink
ports:
- Open
- Close
- Toggle
- AutoClose
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.WiresUiKey.Key - key: enum.WiresUiKey.Key

View File

@@ -76,11 +76,16 @@
key: walls key: walls
mode: NoSprite mode: NoSprite
- type: DoorSignalControl - type: DoorSignalControl
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
Open: [] receiveFrequencyId: BasicDevice
Close: [] - type: WirelessNetworkConnection
Toggle: [] range: 200
- type: DeviceLinkSink
ports:
- Open
- Close
- Toggle
- type: InteractionPopup - type: InteractionPopup
interactSuccessString: comp-window-knock interactSuccessString: comp-window-knock
messagePerceivedByOthers: comp-window-knock messagePerceivedByOthers: comp-window-knock

View File

@@ -46,12 +46,17 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: DoorSignalControl - type: DoorSignalControl
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
Open: [] receiveFrequencyId: BasicDevice
Close: [] - type: WirelessNetworkConnection
Toggle: [] range: 200
AutoClose: [] - type: DeviceLinkSink
ports:
- Open
- Close
- Toggle
- AutoClose
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Glass damageModifierSet: Glass

View File

@@ -86,18 +86,19 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Apc deviceNetId: Wireless
receiveFrequencyId: SmartLight receiveFrequencyId: SmartLight
- type: ApcNetworkConnection - type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSink
ports:
- On
- Off
- Toggle
- type: Appearance - type: Appearance
- type: PoweredLightVisuals - type: PoweredLightVisuals
blinkingSound: blinkingSound:
path: "/Audio/Machines/light_tube_on.ogg" path: "/Audio/Machines/light_tube_on.ogg"
- type: SignalReceiver
inputs:
On: []
Off: []
Toggle: []
- type: entity - type: entity
id: Poweredlight id: Poweredlight
@@ -266,16 +267,17 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Apc deviceNetId: Wireless
receiveFrequencyId: SmartLight receiveFrequencyId: SmartLight
- type: ApcNetworkConnection - type: WirelessNetworkConnection
range: 200
- type: Appearance - type: Appearance
- type: PoweredLightVisuals - type: PoweredLightVisuals
- type: SignalReceiver - type: DeviceLinkSink
inputs: ports:
On: [] - On
Off: [] - Off
Toggle: [] - Toggle
- type: entity - type: entity
id: PoweredSmallLight id: PoweredSmallLight

View File

@@ -91,9 +91,15 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver - type: ExtensionCableReceiver
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Apc deviceNetId: Wireless
receiveFrequencyId: SmartLight receiveFrequencyId: SmartLight
- type: ApcNetworkConnection - type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSink
ports:
- On
- Off
- Toggle
- type: Construction - type: Construction
graph: LightFixture graph: LightFixture
node: groundLight node: groundLight
@@ -101,11 +107,6 @@
- type: PoweredLightVisuals - type: PoweredLightVisuals
blinkingSound: blinkingSound:
path: "/Audio/Machines/light_tube_on.ogg" path: "/Audio/Machines/light_tube_on.ogg"
- type: SignalReceiver
inputs:
On: []
Off: []
Toggle: []
- type: entity - type: entity
id: PoweredLightPostSmall id: PoweredLightPostSmall

View File

@@ -378,10 +378,10 @@
- type: DeviceList - type: DeviceList
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Wired deviceNetId: Wired
- type: SignalTransmitter - type: DeviceLinkSource
transmissionRange: 5 range: 5
outputs: ports:
ArtifactAnalyzerSender: [] - ArtifactAnalyzerSender
- type: ActivatableUI - type: ActivatableUI
key: enum.ArtifactAnalzyerUiKey.Key key: enum.ArtifactAnalzyerUiKey.Key
- type: UserInterface - type: UserInterface
@@ -698,11 +698,11 @@
radius: 1.5 radius: 1.5
energy: 1.6 energy: 1.6
color: "#1f8c28" color: "#1f8c28"
- type: SignalTransmitter - type: DeviceLinkSource
transmissionRange: 4 range: 4
outputs: ports:
MedicalScannerSender: [] - MedicalScannerSender
CloningPodSender: [] - CloningPodSender
- type: ActivatableUI - type: ActivatableUI
key: enum.CloningConsoleUiKey.Key key: enum.CloningConsoleUiKey.Key
- type: UserInterface - type: UserInterface

View File

@@ -49,10 +49,11 @@
- type: ArtifactAnalyzer - type: ArtifactAnalyzer
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Wired deviceNetId: Wired
receiveFrequencyId: BasicDevice
- type: DeviceList - type: DeviceList
- type: SignalReceiver - type: DeviceLinkSink
inputs: ports:
ArtifactAnalyzerReceiver: [] - ArtifactAnalyzerReceiver
- type: Machine - type: Machine
board: ArtifactAnalyzerMachineCircuitboard board: ArtifactAnalyzerMachineCircuitboard
- type: PointLight - type: PointLight

View File

@@ -8,6 +8,10 @@
- type: DeviceList - type: DeviceList
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Wired deviceNetId: Wired
receiveFrequencyId: BasicDevice
- type: DeviceLinkSink
ports:
- CloningPodReceiver
- type: Sprite - type: Sprite
netsync: false netsync: false
sprite: Structures/Machines/cloning.rsi sprite: Structures/Machines/cloning.rsi
@@ -34,9 +38,6 @@
- machine_board - machine_board
- machine_parts - machine_parts
- clonepod-bodyContainer - clonepod-bodyContainer
- type: SignalReceiver
inputs:
CloningPodReceiver: []
- type: EmptyOnMachineDeconstruct - type: EmptyOnMachineDeconstruct
containers: containers:
- clonepod-bodyContainer - clonepod-bodyContainer

View File

@@ -74,6 +74,7 @@
- Wrench - Wrench
- Crowbar - Crowbar
- Multitool - Multitool
- NetworkConfigurator
- AirlockPainter - AirlockPainter
- CableStack - CableStack
- HandheldGPSBasic - HandheldGPSBasic

View File

@@ -7,7 +7,11 @@
- type: MedicalScanner - type: MedicalScanner
- type: DeviceNetwork - type: DeviceNetwork
deviceNetId: Wired deviceNetId: Wired
receiveFrequencyId: BasicDevice
- type: DeviceList - type: DeviceList
- type: DeviceLinkSink
ports:
- MedicalScannerReceiver
- type: Sprite - type: Sprite
netsync: false netsync: false
sprite: Structures/Machines/scanner.rsi sprite: Structures/Machines/scanner.rsi
@@ -58,9 +62,6 @@
scanner-bodyContainer: !type:ContainerSlot scanner-bodyContainer: !type:ContainerSlot
machine_board: !type:Container machine_board: !type:Container
machine_parts: !type:Container machine_parts: !type:Container
- type: SignalReceiver
inputs:
MedicalScannerReceiver: []
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: StrongMetallic damageModifierSet: StrongMetallic

View File

@@ -42,11 +42,11 @@
- MidImpassable - MidImpassable
- LowImpassable - LowImpassable
hard: False hard: False
- type: SignalReceiver - type: DeviceLinkSink
inputs: ports:
Reverse: [] - Reverse
Forward: [] - Forward
Off: [] - Off
- type: Transform - type: Transform
anchored: true anchored: true
noRot: false noRot: false

View File

@@ -208,11 +208,16 @@
- type: PipeColorVisuals - type: PipeColorVisuals
- type: GasValve - type: GasValve
- type: SignalControlledValve - type: SignalControlledValve
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
Open: [] receiveFrequencyId: BasicDevice
Close: [] - type: WirelessNetworkConnection
Toggle: [] range: 200
- type: DeviceLinkSink
ports:
- Open
- Close
- Toggle
- type: NodeContainer - type: NodeContainer
nodes: nodes:
inlet: inlet:
@@ -296,10 +301,15 @@
inlet: inlet inlet: inlet
outlet: outlet outlet: outlet
canLink: true canLink: true
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
Pressurize: [] receiveFrequencyId: BasicDevice
Depressurize: [] - type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSink
ports:
- Pressurize
- Depressurize
- type: Construction - type: Construction
graph: GasBinary graph: GasBinary
node: dualportventpump node: dualportventpump

View File

@@ -24,10 +24,14 @@
graph: SignalSwitchGraph graph: SignalSwitchGraph
node: SignalSwitchNode node: SignalSwitchNode
- type: Fixtures - type: Fixtures
- type: SignalTransmitter - type: DeviceNetwork
outputs: deviceNetId: Wireless
On: [] - type: WirelessNetworkConnection
Off: [] range: 200
- type: DeviceLinkSource
ports:
- On
- Off
- type: entity - type: entity
id: SignalButton id: SignalButton
@@ -57,9 +61,13 @@
graph: SignalButtonGraph graph: SignalButtonGraph
node: SignalButtonNode node: SignalButtonNode
- type: Fixtures - type: Fixtures
- type: SignalTransmitter - type: DeviceNetwork
outputs: deviceNetId: Wireless
Pressed: [] - type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSource
ports:
- Pressed
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Metallic damageModifierSet: Metallic
@@ -153,8 +161,12 @@
- type: Construction - type: Construction
graph: LeverGraph graph: LeverGraph
node: LeverNode node: LeverNode
- type: SignalTransmitter - type: DeviceNetwork
outputs: deviceNetId: Wireless
Left: [] - type: WirelessNetworkConnection
Right: [] range: 200
Middle: [] - type: DeviceLinkSource
ports:
- Left
- Right
- Middle

View File

@@ -32,9 +32,14 @@
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Metallic damageModifierSet: Metallic
- type: SignalReceiver - type: DeviceNetwork
inputs: deviceNetId: Wireless
OrderReceiver: [] receiveFrequencyId: BasicDevice
- type: WirelessNetworkConnection
range: 200
- type: DeviceLinkSink
ports:
- OrderReceiver
- type: Destructible - type: Destructible
thresholds: thresholds:
- trigger: - trigger:

View File

@@ -35,11 +35,11 @@
- LowImpassable - LowImpassable
hard: False hard: False
- type: Conveyor - type: Conveyor
- type: SignalReceiver - type: DeviceLinkSink
inputs: ports:
Reverse: [] - Reverse
Forward: [] - Forward
Off: [] - Off
- type: Appearance - type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:

View File

@@ -7,6 +7,7 @@
- Construction - Construction
- Power - Power
- ShuttleCraft - ShuttleCraft
- Networking
- type: guideEntry - type: guideEntry
id: Construction id: Construction
@@ -30,6 +31,19 @@
name: guide-entry-shuttle-craft name: guide-entry-shuttle-craft
text: "/ServerInfo/Guidebook/Shuttlecraft.xml" text: "/ServerInfo/Guidebook/Shuttlecraft.xml"
- type: guideEntry
id: Networking
name: guide-entry-networking
text: "/ServerInfo/Guidebook/Networking.xml"
children:
- NetworkConfigurator
- type: guideEntry
id: NetworkConfigurator
name: guide-entry-network-configurator
text: "/ServerInfo/Guidebook/Network_Configurator.xml"
- type: guideEntry - type: guideEntry
id: Power id: Power
name: guide-entry-power name: guide-entry-power

View File

@@ -86,6 +86,14 @@
Steel: 200 Steel: 200
Plastic: 200 Plastic: 200
- type: latheRecipe
id: NetworkConfigurator
result: NetworkConfigurator
completetime: 2
materials:
Steel: 200
Plastic: 200
- type: latheRecipe - type: latheRecipe
id: PowerDrill id: PowerDrill
result: PowerDrill result: PowerDrill

View File

@@ -194,8 +194,6 @@
- Item - Item
permanentComponents: permanentComponents:
- type: TilePrying - type: TilePrying
- type: SignalLinker
requiredQuality: Pulsing
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.SignalLinkerUiKey.Key - key: enum.SignalLinkerUiKey.Key
@@ -206,6 +204,9 @@
- Screwing - Screwing
speed: 2 # Very powerful multitool to balance out the desire to sell or scrap for points speed: 2 # Very powerful multitool to balance out the desire to sell or scrap for points
useSound: /Audio/Items/drill_use.ogg useSound: /Audio/Items/drill_use.ogg
- type: Tag
tags:
- Multitool
- type: MultipleTool - type: MultipleTool
statusShowBehavior: true statusShowBehavior: true
entries: entries:

View File

@@ -498,6 +498,9 @@
- type: Tag - type: Tag
id: Mop id: Mop
- type: Tag
id: Multitool
- type: Tag - type: Tag
id: NoSpinOnThrow id: NoSpinOnThrow

View File

@@ -0,0 +1,39 @@
<Document>
# Network Configurator
The network configurator allows you to manipulate device lists and link devices together.
<Box>
<GuideEntityEmbed Entity="NetworkConfigurator"/>
</Box>
The configurator has two modes: List and Link. You can press [color=gray]Alt+Z[/color] or [color=gray]Alt+Y[/color] to switch between them.
## List Mode
In list mode you can click on network devices to save them on the configurator and then on a network device that has a device list like the [color=#a4885c]Air Alarm[/color].
When clicking on a device like the Air Alarm, a UI will open displaying the list currently saved on the device and buttons to manipulate that list.
You can:
- Replace the current list with the one saved on the configurator
- Add the list on the configurator to the current one
- Clear the current list
- Copy the current list to the configurator
- Visualize the connections to the devices on the current list
Pressing [color=gray]z[/color] or [color=gray]y[/color] opens the list saved on the configurator where you can remove saved devices.
## Link Mode
With link mode you can click on a device that is capable of device linking and click on any other device that is either
a sink or source.
For example, first clicking on a source like a [color=#a4885c]signal button[/color] and then on sink like a
[color=#a4885c]small light[/color] opens a UI that displays the source ports on the left side and the sink ports on the right.
Now you can eiter click [color=gray]link defaults[/color] to link the default ports for a source + sink combination or press on a source and then a sink port to connect them.
An example of a default link for the aformentioned combinaton of devices would be:
<Box>
[color=cyan]Pressed 🠒 Toggle[/color]
</Box>
When you're done connecting the ports you want you can click on [color=gray]ok[/color] to close the UI.
You can quickly link multiple devices to their default port by first clicking on a device that can be linked and then using [color=gray]alt+left mouse button[/color] on the devices you want to link together.
</Document>

View File

@@ -0,0 +1,25 @@
<Document>
# Networking
Some devices on the station need to communicate with each other, and they do this by utilizing device networking.
With networking machines and devices can send arbitrary data between each other.
There are multiple networks that get used, such as the wireless and wired network.
Each network device has a frequency it receives on. PDAs for example, use the frequency: [color=green]220.2[/color]
## Device Lists
Some devices need to know what other devices to communicate with specifically.
<Box>
<GuideEntityEmbed Entity="AirAlarm"/>
</Box>
Air alarms for example require you to tell it which vents, scrubbers, sensors, and firelocks to interact with.
You do that by using the Network Configurator.
## Linking
If devices basic or still more advanced but need finer control of how and what connects to each other they will generally use device linking.
<Box>
<GuideEntityEmbed Entity="SignalSwitch"/>
<GuideEntityEmbed Entity="Airlock"/>
</Box>
With linking you can connect the outputs of a device like [color=gray]On[/color] or [color=gray]Off[/color] with the inputs of a device like the airlocks
[color=gray]Open[/color] or [color=gray]Close[/color] inputs.
The Network Configurator is also used for linking devices together.
</Document>

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -0,0 +1,32 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Made by Julian Giebel <juliangiebel@live.de> for ss14",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "mode-list"
},
{
"name": "mode-link"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "equipped-BELT",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B