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

View File

@@ -1,7 +1,7 @@
<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="350 100">
Title="{Loc 'network-configurator-title-device-configuration'}" MinSize="350 100">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" MinHeight="500" />
<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="Show" Text="Show" Access="Public" ToggleMode="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
</BoxContainer>
<Label Name="Count" HorizontalAlignment="Right" />
<Label Name="Count" StyleClasses="LabelSubText" HorizontalAlignment="Right" Margin="0 0 12 8"/>
</BoxContainer>
</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.Components;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Random;

View File

@@ -1,7 +1,7 @@
<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="220 400">
Title="{Loc 'network-configurator-title-saved-devices'}" MinSize="220 400">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<networkConfigurator:NetworkConfiguratorDeviceList Name="DeviceList" />
<BoxContainer Orientation="Horizontal" Margin="8 8 8 8">

View File

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

View File

@@ -1,15 +1,22 @@
using System.Linq;
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.DeviceNetwork;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Systems;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Console;
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
{
@@ -17,6 +24,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private const string Action = "ClearNetworkLinkOverlays";
@@ -25,6 +33,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
base.Initialize();
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)
@@ -102,6 +117,41 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
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

View File

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

View File

@@ -1,12 +1,13 @@
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;
namespace Content.Server.Atmos.Piping.Binary.EntitySystems;
public sealed class SignalControlledValveSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signal = default!;
[Dependency] private readonly DeviceLinkSystem _signal = default!;
[Dependency] private readonly GasValveSystem _valve = default!;
public override void Initialize()
@@ -19,10 +20,10 @@ public sealed class SignalControlledValveSystem : EntitySystem
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))
return;

View File

@@ -1,13 +1,12 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Monitor.Components;
using Content.Server.Atmos.Monitor.Systems;
using Content.Server.Atmos.Piping.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.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes;
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 DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = 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)
{
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)
return;

View File

@@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using Content.Server.Access.Systems;
using Content.Server.Cargo.Components;
using Content.Server.Labels.Components;
using Content.Server.MachineLinking.System;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Popups;
using Content.Server.Station.Systems;
using Content.Shared.Access.Systems;
@@ -34,7 +34,7 @@ namespace Content.Server.Cargo.Systems
[Dependency] private readonly IdCardSystem _idCardSystem = 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 StationSystem _station = 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)
{
_linker.EnsureReceiverPorts(uid, telepad.ReceiverPort);
_linker.EnsureSinkPorts(uid, telepad.ReceiverPort);
}
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.Medical.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.Mind.Components;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Server.UserInterface;
using Content.Server.Power.EntitySystems;
using Robust.Server.GameObjects;
@@ -16,7 +15,8 @@ using Robust.Server.Player;
using Content.Shared.Cloning.CloningConsole;
using Content.Shared.Cloning;
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.Mobs.Components;
using Content.Shared.Mobs.Systems;
@@ -26,7 +26,7 @@ namespace Content.Server.Cloning
[UsedImplicitly]
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 IPlayerManager _playerManager = default!;
[Dependency] private readonly CloningSystem _cloningSystem = default!;
@@ -48,7 +48,7 @@ namespace Content.Server.Cloning
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)
{
@@ -72,20 +72,20 @@ namespace Content.Server.Cloning
private void OnMapInit(EntityUid uid, CloningConsoleComponent component, MapInitEvent args)
{
if (!TryComp<SignalTransmitterComponent>(uid, out var receiver))
if (!TryComp<DeviceLinkSourceComponent>(uid, out var receiver))
return;
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;
}
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;
}
}
@@ -93,15 +93,15 @@ namespace Content.Server.Cloning
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;
}
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;
}
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.EUI;
using Content.Server.Humanoid;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Shared.Chemistry.Components;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Chat.Systems;
using Content.Server.Construction;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Materials;
using Content.Server.Stack;
using Content.Server.Jobs;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Components;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
@@ -43,7 +43,7 @@ namespace Content.Server.Cloning
{
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 IPrototypeManager _prototype = default!;
[Dependency] private readonly EuiManager _euiManager = null!;
@@ -84,7 +84,7 @@ namespace Content.Server.Cloning
private void OnComponentInit(EntityUid uid, CloningPodComponent clonePod, ComponentInit args)
{
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)

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,8 @@
using Content.Shared.DeviceLinking;
using Content.Shared.MachineLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.MachineLinking.Components
namespace Content.Server.DeviceLinking.Components
{
[RegisterComponent]
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>
/// This handles automatically linking autolinked entities at round-start.
/// </summary>
public sealed class AutoLinkSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signalLinkerSystem = default!;
[Dependency] private readonly DeviceLinkSystem _deviceLinkSystem = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -29,7 +30,7 @@ public sealed class AutoLinkSystem : EntitySystem
if (rxXform.GridUid != xform.GridUid)
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.MachineLinking.Events;
using Content.Server.DeviceLinking.Components;
using Content.Server.DeviceLinking.Events;
using Content.Server.Doors.Systems;
using Content.Server.MachineLinking.System;
using Content.Shared.Doors.Components;
using JetBrains.Annotations;
namespace Content.Server.MachineLinking.System
namespace Content.Server.DeviceLinking.Systems
{
[UsedImplicitly]
public sealed class DoorSignalControlSystem : EntitySystem
{
[Dependency] private readonly DoorSystem _doorSystem = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
public override void Initialize()
{
@@ -21,10 +22,10 @@ namespace Content.Server.MachineLinking.System
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))
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.Interaction;
using Robust.Shared.Audio;
using Robust.Shared.Player;
namespace Content.Server.MachineLinking.System
namespace Content.Server.DeviceLinking.Systems
{
public sealed class SignalSwitchSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
public override void Initialize()
{
@@ -20,7 +21,7 @@ namespace Content.Server.MachineLinking.System
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)

View File

@@ -1,13 +1,13 @@
using Content.Server.DeviceLinking.Components;
using Content.Server.Explosion.EntitySystems;
using Content.Server.MachineLinking.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Timing;
namespace Content.Server.MachineLinking.System;
namespace Content.Server.DeviceLinking.Systems;
public sealed class SignallerSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signal = default!;
[Dependency] private readonly DeviceLinkSystem _link = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
public override void Initialize()
@@ -21,14 +21,14 @@ public sealed class SignallerSystem : EntitySystem
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)
{
if (args.Handled)
return;
_signal.InvokePort(uid, component.Port);
_link.InvokePort(uid, component.Port);
args.Handled = true;
}
@@ -43,7 +43,7 @@ public sealed class SignallerSystem : EntitySystem
if (hasUseDelay)
_useDelay.BeginDelay(uid, useDelay);
_signal.InvokePort(uid, component.Port);
_link.InvokePort(uid, component.Port);
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.MachineLinking;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Utility;
namespace Content.Server.MachineLinking.System
namespace Content.Server.DeviceLinking.Systems
{
public sealed class TwoWayLeverSystem : EntitySystem
{
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
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)
{
_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)

View File

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

View File

@@ -1,16 +1,22 @@
using System.Linq;
using Content.Server.DeviceLinking.Systems;
using Content.Server.DeviceNetwork.Components;
using Content.Server.UserInterface;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Database;
using Content.Shared.DeviceLinking;
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.Popups;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.DeviceNetwork.Systems;
@@ -19,11 +25,14 @@ namespace Content.Server.DeviceNetwork.Systems;
public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
{
[Dependency] private readonly DeviceListSystem _deviceListSystem = default!;
[Dependency] private readonly DeviceLinkSystem _deviceLinkSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessSystem = 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()
{
@@ -32,17 +41,23 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
SubscribeLocalEvent<NetworkConfiguratorComponent, MapInitEvent>(OnMapInit);
//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
SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<UtilityVerb>>(OnAddInteractVerb);
SubscribeLocalEvent<DeviceNetworkComponent, GetVerbsEvent<AlternativeVerb>>(OnAddAlternativeSaveDeviceVerb);
SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<AlternativeVerb>>(OnAddSwitchModeVerb);
//UI
SubscribeLocalEvent<NetworkConfiguratorComponent, BoundUIClosedEvent>(OnUiClosed);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorRemoveDeviceMessage>(OnRemoveDevice);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearDevicesMessage>(OnClearDevice);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorLinksSaveMessage>(OnSaveLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearLinksMessage>(OnClearLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorToggleLinkMessage>(OnToggleLinks);
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorButtonPressedMessage>(OnConfigButtonPressed);
SubscribeLocalEvent<NetworkConfiguratorComponent, ActivatableUIOpenAttemptEvent>(OnUiOpenAttempt);
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.
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.Configure)?.CloseAll();
_uiSystem.TryCloseAll(uid, NetworkConfiguratorUiKey.Configure);
}
}
private void OnMapInit(EntityUid uid, NetworkConfiguratorComponent component, MapInitEvent args)
{
component.Devices.Clear();
UpdateUiState(uid, component);
UpdateListUiState(uid, component);
}
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)),
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)
{
if (!TryComp(target, out AccessReaderComponent? reader) || user == null)
return false;
return true;
if (_accessSystem.IsAllowed(user.Value, reader))
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);
return false;
@@ -137,26 +199,80 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
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
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>
/// Either adds a device to the device list or shows the config ui if the target is ant entity with a device list
/// </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;
if (!HasComp<DeviceListComponent>(target))
if (configurator.LinkModeActive)
{
TryAddNetworkDevice(target, user, component);
TryLinkDevice(uid, configurator, target, user);
return;
}
OpenDeviceListUi(target, user, component);
if (!HasComp<DeviceListComponent>(target))
{
TryAddNetworkDevice(target, user, configurator);
return;
}
OpenDeviceListUi(target, user, configurator);
}
#endregion
@@ -166,22 +282,32 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// <summary>
/// Adds the interaction verb which is either configuring device lists or saving a device onto the configurator
/// </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;
var isDeviceList = HasComp<DeviceListComponent>(args.Target);
UtilityVerb verb = new()
var verb = new UtilityVerb
{
Text = Loc.GetString(isDeviceList ? "network-configurator-configure" : "network-configurator-save-device"),
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),
Act = () => OnUsed(uid, configurator, args.Target, args.User),
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);
}
@@ -193,8 +319,39 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// </summary>
private void OnAddAlternativeSaveDeviceVerb(EntityUid uid, DeviceNetworkComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue || !HasComp<NetworkConfiguratorComponent>(args.Using.Value)
|| !HasComp<DeviceListComponent>(args.Target))
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue
|| !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;
AlternativeVerb verb = new()
@@ -211,11 +368,57 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
#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>
/// Opens the config ui. It can be used to modify the devices in the targets device list.
/// </summary>
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))
return;
@@ -233,7 +436,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
/// <summary>
/// Sends the list of saved devices to the ui
/// </summary>
private void UpdateUiState(EntityUid uid, NetworkConfiguratorComponent component)
private void UpdateListUiState(EntityUid uid, NetworkConfiguratorComponent component)
{
HashSet<(string address, string name)> devices = new();
HashSet<string> invalidDevices = new();
@@ -264,6 +467,12 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnUiClosed(EntityUid uid, NetworkConfiguratorComponent component, BoundUIClosedEvent args)
{
component.ActiveDeviceList = null;
if (args.UiKey is NetworkConfiguratorUiKey.Link)
{
component.ActiveDeviceLink = null;
component.DeviceLinkTarget = null;
}
}
/// <summary>
@@ -272,7 +481,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnRemoveDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorRemoveDeviceMessage args)
{
component.Devices.Remove(args.Address);
UpdateUiState(uid, component);
UpdateListUiState(uid, component);
}
/// <summary>
@@ -281,7 +490,61 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
private void OnClearDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorClearDevicesMessage _)
{
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>
@@ -307,11 +570,9 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
break;
case NetworkConfiguratorButtonKey.Copy:
component.Devices = _deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value);
UpdateUiState(uid, component);
UpdateListUiState(uid, component);
return;
case NetworkConfiguratorButtonKey.Show:
// This should be done client-side.
// _deviceListSystem.ToggleVisualization(component.ActiveDeviceList.Value);
break;
}
@@ -330,5 +591,11 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
_deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value)
.Select(v => (v.Key, MetaData(v.Value).EntityName)).ToHashSet()));
}
private void OnUiOpenAttempt(EntityUid uid, NetworkConfiguratorComponent configurator, ActivatableUIOpenAttemptEvent args)
{
if (configurator.LinkModeActive)
args.Cancel();
}
#endregion
}

View File

@@ -1,3 +1,4 @@
using Content.Server.DeviceLinking.Events;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Tools.Components;
@@ -8,7 +9,6 @@ using Content.Shared.Doors.Systems;
using Content.Shared.Interaction;
using Robust.Server.GameObjects;
using Content.Shared.Wires;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
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)
{

View File

@@ -1,20 +1,19 @@
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Explosion.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
namespace Content.Server.Explosion.EntitySystems
{
public sealed partial class TriggerSystem
{
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
private void InitializeSignal()
{
SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived);
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)
return;
@@ -23,7 +22,7 @@ namespace Content.Server.Explosion.EntitySystems
}
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.Ghost;
using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.MachineLinking.System;
using Content.Server.Power.Components;
using Content.Server.Temperature.Components;
using Content.Shared.Audio;
@@ -22,6 +20,8 @@ using Robust.Shared.Player;
using Robust.Shared.Timing;
using Content.Shared.DoAfter;
using Content.Server.Emp;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
namespace Content.Server.Light.EntitySystems
{
@@ -37,7 +37,7 @@ namespace Content.Server.Light.EntitySystems
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger= 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 SharedDoAfterSystem _doAfterSystem = 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)
{
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)
@@ -354,7 +354,7 @@ namespace Content.Server.Light.EntitySystems
_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)
SetState(uid, false, component);

View File

@@ -3,13 +3,13 @@ using Content.Server.MachineLinking.System;
namespace Content.Server.MachineLinking.Components
{
[DataDefinition]
public struct PortIdentifier
public readonly struct PortIdentifier
{
[DataField("uid")]
public EntityUid Uid;
public readonly EntityUid Uid;
[DataField("port")]
public string Port;
public readonly 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.Diagnostics.CodeAnalysis;
using Content.Server.DeviceLinking.Events;
using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.Power.Components;
using Content.Server.Tools;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Interaction;
using Content.Shared.MachineLinking;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Content.Shared.Verbs;
using Robust.Shared.Prototypes;
using Content.Shared.MachineLinking.Events;
using Content.Server.Database;
namespace Content.Server.MachineLinking.System
{
@@ -140,7 +138,10 @@ namespace Content.Server.MachineLinking.System
return;
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)

View File

@@ -1,30 +1,28 @@
using Content.Server.Climbing;
using Content.Server.Cloning;
using Content.Server.Medical.Components;
using Content.Server.Power.Components;
using Content.Shared.Destructible;
using Content.Shared.ActionBlocker;
using Content.Shared.DragDrop;
using Content.Shared.Movement.Events;
using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Content.Server.MachineLinking.System;
using Content.Server.MachineLinking.Events;
using Content.Server.Cloning.Components;
using Content.Server.Construction;
using Content.Server.DeviceLinking.Systems;
using Content.Shared.DeviceLinking.Events;
using Content.Server.Power.EntitySystems;
using Content.Shared.Body.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Server.Containers;
using Robust.Server.GameObjects;
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent; // Hmm...
namespace Content.Server.Medical
{
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 ClimbSystem _climbSystem = default!;
[Dependency] private readonly CloningConsoleSystem _cloningConsoleSystem = default!;
@@ -70,7 +68,7 @@ namespace Content.Server.Medical
{
base.Initialize();
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)

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.Materials;
using Content.Server.Power.Components;
@@ -9,7 +11,6 @@ using Content.Shared.Physics.Controllers;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
namespace Content.Server.Physics.Controllers;
@@ -17,8 +18,8 @@ namespace Content.Server.Physics.Controllers;
public sealed class ConveyorController : SharedConveyorController
{
[Dependency] private readonly FixtureSystem _fixtures = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
[Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!;
[Dependency] private readonly SignalLinkerSystem _signalSystem = default!;
[Dependency] private readonly SharedBroadphaseSystem _broadphase = 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)
{
_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))
{
@@ -76,7 +77,7 @@ public sealed class ConveyorController : SharedConveyorController
_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)
SetState(uid, ConveyorState.Off, component);

View File

@@ -1,7 +1,7 @@
using System.Linq;
using Content.Server.Construction;
using Content.Server.DeviceLinking.Events;
using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Content.Server.Paper;
using Content.Server.Power.Components;
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.Events;
using Content.Shared.Audio;
using Content.Shared.MachineLinking.Events;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Popups;
using Content.Shared.Research.Components;
using Content.Shared.Xenoarchaeology.Equipment;
@@ -168,10 +168,10 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
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;
component.AnalyzerEntity = args.Receiver;
component.AnalyzerEntity = args.Sink;
analyzer.Console = uid;
UpdateUserInterface(uid, component);

View File

@@ -5,9 +5,9 @@ using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.PDA;
using Content.Shared.Access.Components;
using Content.Shared.DeviceLinking.Events;
using Robust.Shared.Prototypes;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.MachineLinking.Events;
using Content.Shared.StationRecords;
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
{

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;
namespace Content.Shared.MachineLinking
namespace Content.Shared.DeviceLinking
{
[Serializable, NetSerializable]
public enum TwoWayLeverVisuals : byte

View File

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

View File

@@ -1,14 +1,24 @@
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork.Systems;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.DeviceNetwork;
namespace Content.Shared.DeviceNetwork.Components;
[RegisterComponent]
[NetworkedComponent]
[Access(typeof(SharedNetworkConfiguratorSystem))]
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>
/// The entity containing a <see cref="DeviceListComponent"/> this configurator is currently interacting with
/// </summary>
@@ -16,22 +26,48 @@ public sealed class NetworkConfiguratorComponent : Component
public EntityUid? ActiveDeviceList = null;
/// <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>
[DataField("devices")]
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")]
public SoundSpecifier SoundNoAccess = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
[DataField("soundSwitchMode")]
public SoundSpecifier SoundSwitchMode = new SoundPathSpecifier("/Audio/Machines/beep.ogg");
}
[Serializable, NetSerializable]
public sealed class NetworkConfiguratorComponentState : ComponentState
{
public readonly EntityUid? ActiveDeviceList;
public readonly bool LinkModeActive;
public NetworkConfiguratorComponentState(EntityUid? activeDeviceList)
public NetworkConfiguratorComponentState(EntityUid? activeDeviceList, bool linkModeActive)
{
ActiveDeviceList = activeDeviceList;
LinkModeActive = linkModeActive;
}
}

View File

@@ -6,7 +6,8 @@ namespace Content.Shared.DeviceNetwork;
public enum NetworkConfiguratorUiKey
{
List,
Configure
Configure,
Link
}
[Serializable, NetSerializable]
@@ -52,3 +53,33 @@ public sealed class NetworkConfiguratorButtonPressedMessage : BoundUserInterface
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;
@@ -23,3 +24,24 @@ public sealed class DeviceListUserInterfaceState : BoundUserInterfaceState
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 Content.Shared.DeviceNetwork.Components;
using Robust.Shared.GameStates;
namespace Content.Shared.DeviceNetwork;

View File

@@ -1,8 +1,9 @@
using Content.Shared.Actions;
using Content.Shared.DeviceNetwork.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.DeviceNetwork;
namespace Content.Shared.DeviceNetwork.Systems;
public abstract class SharedNetworkConfiguratorSystem : EntitySystem
{
@@ -17,7 +18,7 @@ public abstract class SharedNetworkConfiguratorSystem : EntitySystem
private void GetNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp,
ref ComponentGetState args)
{
args.State = new NetworkConfiguratorComponentState(comp.ActiveDeviceList);
args.State = new NetworkConfiguratorComponentState(comp.ActiveDeviceList, comp.LinkModeActive);
}
private void HandleNetworkConfiguratorState(EntityUid uid, NetworkConfiguratorComponent comp,
@@ -29,9 +30,22 @@ public abstract class SharedNetworkConfiguratorSystem : EntitySystem
}
comp.ActiveDeviceList = state.ActiveDeviceList;
comp.LinkModeActive = state.LinkModeActive;
}
}
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

@@ -6,6 +6,7 @@ device-frequency-prototype-name-lights = Smart Lights
device-frequency-prototype-name-mailing-units = Mailing Units
device-frequency-prototype-name-pdas = PDAs
device-frequency-prototype-name-fax = Fax
device-frequency-prototype-name-basic-device = Basic Devices
## camera frequencies
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-device-already-saved = network device: {$device} is already saved.
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
network-configurator-save-device = Save device
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
network-configurator-title-saved-devices = Saved Devices
network-configurator-title-device-configuration = Device Configuration
network-configurator-ui-clear-button = Clear
network-configurator-ui-count-label = {$count} Devices
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-copy = Copy targets device list to multitool
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-fires = Fires & Space
guide-entry-shuttle-craft = Shuttle-craft
guide-entry-networking = Networking
guide-entry-network-configurator = Network Configurator
guide-entry-power = Power
guide-entry-ame = Antimatter Engine (AME)
guide-entry-singularity = Singularity

View File

@@ -1,3 +1,5 @@
signal-port-selector-menu-title = Port Selector
signal-port-selector-menu-clear = Clear
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-description-toggle = Toggles the state of a device.

View File

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

View File

@@ -86,3 +86,8 @@
id: Fax
name: device-frequency-prototype-name-fax
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
sprite: Clothing/Belt/utility.rsi
- type: Storage
capacity: 45
# TODO: Fill this out more.
whitelist:
tags:
@@ -26,10 +27,11 @@
- CigPack
- Radio
- HolofanProjector
- Multitool
- AppraisalTool
components:
- AirlockPainter
- SignalLinker
- NetworkConfigurator
- RCD
- RCDAmmo
- Welder
@@ -63,8 +65,8 @@
- Wrench
multitool:
whitelist:
components:
- SignalLinker
tags:
- Multitool
sprite: Clothing/Belt/belt_overlay.rsi
- type: Appearance
@@ -79,7 +81,7 @@
- type: Clothing
sprite: Clothing/Belt/ce.rsi
- type: Storage
capacity: 100
capacity: 105
# TODO: Fill this out more.
whitelist:
tags:
@@ -96,10 +98,11 @@
- CigPack
- Radio
- HolofanProjector
- Multitool
- AppraisalTool
components:
- AirlockPainter
- SignalLinker
- NetworkConfigurator
- RCD
- RCDAmmo
- Welder
@@ -135,8 +138,8 @@
- Screwdriver
multitool:
whitelist:
components:
- SignalLinker
tags:
- Multitool
wrench:
whitelist:
tags:

View File

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

View File

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

View File

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

View File

@@ -196,26 +196,62 @@
- type: Tool
qualities:
- Pulsing
- type: SignalLinker
- type: Tag
tags:
- Multitool
- DroneUsable
- type: PhysicalComposition
materialComposition:
Steel: 100
Plastic: 100
- type: StaticPrice
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.SignalLinkerUiKey.Key
type: SignalPortSelectorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.List
type: NetworkConfiguratorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.Configure
type: NetworkConfiguratorBoundUserInterface
- key: enum.NetworkConfiguratorUiKey.Link
type: NetworkConfiguratorBoundUserInterface
- type: Tag
tags:
- DroneUsable
- type: PhysicalComposition
materialComposition:
Steel: 100
Plastic: 100
- type: StaticPrice
price: 60
@@ -371,13 +407,10 @@
- type: Item
sprite: Objects/Tools/omnitool.rsi
size: 20
- type: Tag
tags:
- Multitool
- type: TilePrying
- type: SignalLinker
requiredQuality: Pulsing
- type: UserInterface
interfaces:
- key: enum.SignalLinkerUiKey.Key
type: SignalPortSelectorBoundUserInterface
- type: Tool
qualities:
- Screwing

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@
- Construction
- Power
- ShuttleCraft
- Networking
- type: guideEntry
id: Construction
@@ -30,6 +31,19 @@
name: guide-entry-shuttle-craft
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
id: Power
name: guide-entry-power

View File

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

View File

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

View File

@@ -498,6 +498,9 @@
- type: Tag
id: Mop
- type: Tag
id: Multitool
- type: Tag
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