Device network DeviceLists and the NetworkConfigurator (Makes air alarms usable) (#7697)
* Implement DeviceList Implement NetworkConfigurator I sould really get into the habit of making smaller commits * Remove ApcNetworkComponent from vents, scrubbers anf firelocks * Change BeforeBroadcastAttemptEvent#Recepients to readonly IReadonlySet and add a ModifiedRecepients field * Address revievs in NetworkConfigurationSystem * Fix red and green button styles * Change NetworkConfiguratorSystem#UpdateState to remove saved entites that don't exist anymore * Add AtmosDevices device net id * Add const strings for style classes Fix wrong margin for NetworkConfiguratorConfigurationMenu * Hello? Github? * Add access check before opening the configuration ui * Address reviews * Fix call to access reader * You shall not live again IgnoreComponent * Fix interaction verb check * Fix configuration window not closing when target gets deleted / out of range * Change device is already saved message to say 'network device: ... is already saves' * Apply suggestions from code review Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> * Fix applied suggestion Co-authored-by: wrexbe <81056464+wrexbe@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
using Content.Shared.DeviceNetwork;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.NetworkConfigurator;
|
||||||
|
|
||||||
|
public sealed class NetworkConfiguratorBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private NetworkConfiguratorListMenu? _listMenu;
|
||||||
|
private NetworkConfiguratorConfigurationMenu? _configurationMenu;
|
||||||
|
|
||||||
|
public NetworkConfiguratorBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnRemoveButtonPressed(string address)
|
||||||
|
{
|
||||||
|
SendMessage(new NetworkConfiguratorRemoveDeviceMessage(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
switch (UiKey)
|
||||||
|
{
|
||||||
|
case NetworkConfiguratorUiKey.List:
|
||||||
|
_listMenu = new NetworkConfiguratorListMenu(this);
|
||||||
|
_listMenu.OnClose += Close;
|
||||||
|
_listMenu.ClearButton.OnPressed += _ => OnClearButtonPressed();
|
||||||
|
_listMenu.OpenCentered();
|
||||||
|
break;
|
||||||
|
case NetworkConfiguratorUiKey.Configure:
|
||||||
|
_configurationMenu = new NetworkConfiguratorConfigurationMenu();
|
||||||
|
_configurationMenu.OnClose += Close;
|
||||||
|
_configurationMenu.Set.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Set);
|
||||||
|
_configurationMenu.Add.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Add);
|
||||||
|
//_configurationMenu.Edit.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Edit);
|
||||||
|
_configurationMenu.Clear.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Clear);
|
||||||
|
_configurationMenu.Copy.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Copy);
|
||||||
|
_configurationMenu.Show.OnPressed += _ => OnConfigButtonPressed(NetworkConfiguratorButtonKey.Show);
|
||||||
|
_configurationMenu.OpenCentered();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
var castState = (NetworkConfiguratorUserInterfaceState) state;
|
||||||
|
_listMenu?.UpdateState(castState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing) return;
|
||||||
|
|
||||||
|
_listMenu?.Dispose();
|
||||||
|
_configurationMenu?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClearButtonPressed()
|
||||||
|
{
|
||||||
|
SendMessage(new NetworkConfiguratorClearDevicesMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConfigButtonPressed(NetworkConfiguratorButtonKey buttonKey)
|
||||||
|
{
|
||||||
|
SendMessage(new NetworkConfiguratorButtonPressedMessage(buttonKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.UserInterface"
|
||||||
|
Title="Network Configurator" MinSize="350 100">
|
||||||
|
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
|
||||||
|
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 8 8 1">
|
||||||
|
<Button Name="Set" Text="Set" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-set'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
|
||||||
|
<Button Name="Add" Text="Add" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-add'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
|
||||||
|
<!-- Edit might not be needed -->
|
||||||
|
<!--<Button Name="Edit" Text="Edit" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-edit'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>-->
|
||||||
|
<Button Name="Clear" Text="Clear" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-clear'}" HorizontalExpand="True"/>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 0 8 8">
|
||||||
|
<Button Name="Copy" Text="Copy" Access="Public" ToolTip="{Loc 'network-configurator-tooltip-copy'}" HorizontalExpand="True" StyleClasses="OpenRight"/>
|
||||||
|
<Button Name="Show" Text="Show" Access="Public" Disabled="True" ToolTip="{Loc 'network-configurator-tooltip-show'}" HorizontalExpand="True" StyleClasses="ButtonSquare"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</ui:FancyWindow>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
|
namespace Content.Client.NetworkConfigurator;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class NetworkConfiguratorConfigurationMenu : FancyWindow
|
||||||
|
{
|
||||||
|
|
||||||
|
public NetworkConfiguratorConfigurationMenu()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
Clear.StyleClasses.Add(StyleBase.ButtonOpenLeft);
|
||||||
|
Clear.StyleClasses.Add(StyleNano.StyleClassButtonColorRed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<ui:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.UserInterface"
|
||||||
|
Title="Network Configurator" MinSize="220 400">
|
||||||
|
<BoxContainer Orientation="Vertical" VerticalExpand="True">
|
||||||
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
|
||||||
|
<Control VerticalExpand="True">
|
||||||
|
<PanelContainer StyleClasses="PanelBackgroundBaseDark"></PanelContainer>
|
||||||
|
<BoxContainer Orientation="Vertical" Name="DeviceList" VerticalExpand="True" SeparationOverride="4">
|
||||||
|
</BoxContainer>
|
||||||
|
</Control>
|
||||||
|
</ScrollContainer>
|
||||||
|
<BoxContainer Orientation="Horizontal" Margin="8 8 8 8">
|
||||||
|
<Label Name="DeviceCountLabel" Margin="16 0 0 0" MaxWidth="64"></Label>
|
||||||
|
<Control HorizontalExpand="True" />
|
||||||
|
<Button Name="ClearButton" Access="Public" Text="{Loc 'network-configurator-ui-clear-button'}"></Button>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</ui:FancyWindow>
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using Content.Client.UserInterface;
|
||||||
|
using Content.Shared.DeviceNetwork;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.NetworkConfigurator;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class NetworkConfiguratorListMenu : FancyWindow
|
||||||
|
{
|
||||||
|
private readonly NetworkConfiguratorBoundUserInterface _ui;
|
||||||
|
public NetworkConfiguratorListMenu(NetworkConfiguratorBoundUserInterface ui)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
_ui = ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(NetworkConfiguratorUserInterfaceState state)
|
||||||
|
{
|
||||||
|
DeviceCountLabel.Text = Loc.GetString("network-configurator-ui-count-label", ("count", state.DeviceList.Count));
|
||||||
|
DeviceList.RemoveAllChildren();
|
||||||
|
|
||||||
|
foreach (var savedDevice in state.DeviceList)
|
||||||
|
{
|
||||||
|
DeviceList.AddChild(BuildDeviceListRow(savedDevice));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BoxContainer BuildDeviceListRow((string address, string name) savedDevice)
|
||||||
|
{
|
||||||
|
var row = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||||
|
Margin = new Thickness(8)
|
||||||
|
};
|
||||||
|
|
||||||
|
var name = new Label()
|
||||||
|
{
|
||||||
|
Text = savedDevice.name[..Math.Min(11, savedDevice.name.Length)],
|
||||||
|
SetWidth = 84
|
||||||
|
};
|
||||||
|
|
||||||
|
var address = new Label()
|
||||||
|
{
|
||||||
|
Text = savedDevice.address,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Align = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeButton = new TextureButton()
|
||||||
|
{
|
||||||
|
StyleClasses = { "CrossButtonRed" },
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
Scale = new Vector2(0.5f, 0.5f)
|
||||||
|
};
|
||||||
|
|
||||||
|
removeButton.OnPressed += _ => _ui.OnRemoveButtonPressed(savedDevice.address);
|
||||||
|
|
||||||
|
row.AddChild(name);
|
||||||
|
row.AddChild(address);
|
||||||
|
row.AddChild(removeButton);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ namespace Content.Client.Stylesheets
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class StyleNano : StyleBase
|
public sealed class StyleNano : StyleBase
|
||||||
{
|
{
|
||||||
public const string StyleClassBorderedWindowPanel = "BorderedWindowPanel";
|
public const string StyleClassBorderedWindowPanel = "BorderedWindowPanel";
|
||||||
@@ -93,6 +94,9 @@ namespace Content.Client.Stylesheets
|
|||||||
public static readonly Color ButtonColorCautionPressed = Color.FromHex("#3e6c45");
|
public static readonly Color ButtonColorCautionPressed = Color.FromHex("#3e6c45");
|
||||||
public static readonly Color ButtonColorCautionDisabled = Color.FromHex("#602a2a");
|
public static readonly Color ButtonColorCautionDisabled = Color.FromHex("#602a2a");
|
||||||
|
|
||||||
|
public static readonly Color ButtonColorGoodDefault = Color.FromHex("#3E6C45");
|
||||||
|
public static readonly Color ButtonColorGoodHovered = Color.FromHex("#31843E");
|
||||||
|
|
||||||
// Context menu button colors
|
// Context menu button colors
|
||||||
public static readonly Color ButtonColorContext = Color.FromHex("#1119");
|
public static readonly Color ButtonColorContext = Color.FromHex("#1119");
|
||||||
public static readonly Color ButtonColorContextHover = Color.DarkSlateGray;
|
public static readonly Color ButtonColorContextHover = Color.DarkSlateGray;
|
||||||
@@ -112,6 +116,14 @@ namespace Content.Client.Stylesheets
|
|||||||
|
|
||||||
public const string StyleClassItemStatus = "ItemStatus";
|
public const string StyleClassItemStatus = "ItemStatus";
|
||||||
|
|
||||||
|
//Background
|
||||||
|
public const string StyleClassBackgroundBaseDark = "PanelBackgroundBaseDark";
|
||||||
|
|
||||||
|
//Buttons
|
||||||
|
public const string StyleClassCrossButtonRed = "CrossButtonRed";
|
||||||
|
public const string StyleClassButtonColorRed = "ButtonColorRed";
|
||||||
|
public const string StyleClassButtonColorGreen = "ButtonColorGreen";
|
||||||
|
|
||||||
public override Stylesheet Stylesheet { get; }
|
public override Stylesheet Stylesheet { get; }
|
||||||
|
|
||||||
public StyleNano(IResourceCache resCache) : base(resCache)
|
public StyleNano(IResourceCache resCache) : base(resCache)
|
||||||
@@ -1294,8 +1306,47 @@ namespace Content.Client.Stylesheets
|
|||||||
.Prop("panel", new StyleBoxTexture(BaseButtonOpenLeft) { Padding = default })
|
.Prop("panel", new StyleBoxTexture(BaseButtonOpenLeft) { Padding = default })
|
||||||
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#1F1F23")),
|
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#1F1F23")),
|
||||||
|
|
||||||
Element<PanelContainer>().Class("Inset")
|
Element<PanelContainer>().Class("WindowHeadingBackgroundLight")
|
||||||
.Prop("panel", insetBack),
|
.Prop("panel", new StyleBoxTexture(BaseButtonOpenLeft) { Padding = default }),
|
||||||
|
|
||||||
|
//The lengths you have to go through to change a background color smh
|
||||||
|
Element<PanelContainer>().Class("PanelBackgroundBaseDark")
|
||||||
|
.Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default })
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#1F1F23")),
|
||||||
|
|
||||||
|
// X Texture button ---
|
||||||
|
Element<TextureButton>().Class("CrossButtonRed")
|
||||||
|
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Nano/cross.svg.png"))
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, DangerousRedFore),
|
||||||
|
|
||||||
|
Element<TextureButton>().Class("CrossButtonRed").Pseudo(TextureButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#7F3636")),
|
||||||
|
|
||||||
|
Element<TextureButton>().Class("CrossButtonRed").Pseudo(TextureButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")),
|
||||||
|
// ---
|
||||||
|
|
||||||
|
// Red Button ---
|
||||||
|
Element<Button>().Class("ButtonColorRed")
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorDefaultRed),
|
||||||
|
|
||||||
|
Element<Button>().Class("ButtonColorRed").Pseudo(ContainerButton.StylePseudoClassNormal)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorDefaultRed),
|
||||||
|
|
||||||
|
Element<Button>().Class("ButtonColorRed").Pseudo(ContainerButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorHoveredRed),
|
||||||
|
// ---
|
||||||
|
|
||||||
|
// Green Button ---
|
||||||
|
Element<Button>().Class("ButtonColorGreen")
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorGoodDefault),
|
||||||
|
|
||||||
|
Element<Button>().Class("ButtonColorGreen").Pseudo(ContainerButton.StylePseudoClassNormal)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorGoodDefault),
|
||||||
|
|
||||||
|
Element<Button>().Class("ButtonColorGreen").Pseudo(ContainerButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorGoodHovered),
|
||||||
|
// ---
|
||||||
|
|
||||||
Element<Label>().Class("StatusFieldTitle")
|
Element<Label>().Class("StatusFieldTitle")
|
||||||
.Prop("font-color", NanoGold),
|
.Prop("font-color", NanoGold),
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
[Friend(typeof(DeviceListSystem))]
|
||||||
|
public sealed class DeviceListComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The list of devices can or can't connect to, depending on the <see cref="IsAllowList"/> field.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("devices")]
|
||||||
|
public HashSet<EntityUid> Devices = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the device list is used as an allow or deny list
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("isAllowList")]
|
||||||
|
public bool IsAllowList = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this device list also handles incoming device net packets
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("handleIncoming")]
|
||||||
|
public bool HandleIncomingPackets = false;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ namespace Content.Server.DeviceNetwork.Components
|
|||||||
Wired,
|
Wired,
|
||||||
Wireless,
|
Wireless,
|
||||||
Apc,
|
Apc,
|
||||||
|
AtmosDevices,
|
||||||
Reserved = 100,
|
Reserved = 100,
|
||||||
// Ids outside this enum may exist
|
// Ids outside this enum may exist
|
||||||
// This exists to let yml use nice names instead of numbers
|
// This exists to let yml use nice names instead of numbers
|
||||||
@@ -85,5 +86,13 @@ namespace Content.Server.DeviceNetwork.Components
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("autoConnect")]
|
[DataField("autoConnect")]
|
||||||
public bool AutoConnect = true;
|
public bool AutoConnect = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to send the broadcast recipients list to the sender so it can be filtered.
|
||||||
|
/// <see cref="DeviceListSystem"/>
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("sendBroadcastAttemptEvent")]
|
||||||
|
public bool SendBroadcastAttemptEvent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Content.Shared.Sound;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
[Friend(typeof(NetworkConfiguratorSystem))]
|
||||||
|
public sealed class NetworkConfiguratorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The list of devices stored in the configurator-
|
||||||
|
/// </summary>
|
||||||
|
[DataField("devices")]
|
||||||
|
public Dictionary<string, EntityUid> Devices = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity containing a <see cref="DeviceListComponent"/> this configurator is currently interacting with
|
||||||
|
/// </summary>
|
||||||
|
[DataField("activeDeviceList")]
|
||||||
|
public EntityUid? ActiveDeviceList = null;
|
||||||
|
|
||||||
|
[DataField("soundNoAccess")]
|
||||||
|
public SoundSpecifier SoundNoAccess = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||||
|
}
|
||||||
93
Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs
Normal file
93
Content.Server/DeviceNetwork/Systems/DeviceListSystem.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Systems;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class DeviceListSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<DeviceListComponent, BeforeBroadcastAttemptEvent>(OnBeforeBroadcast);
|
||||||
|
SubscribeLocalEvent<DeviceListComponent, BeforePacketSentEvent>(OnBeforePacketSent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces or merges the current device list with the given one
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateDeviceList(EntityUid uid, IEnumerable<EntityUid> devices, bool merge = false, DeviceListComponent? deviceList = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref deviceList))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!merge)
|
||||||
|
deviceList.Devices.Clear();
|
||||||
|
|
||||||
|
deviceList.Devices.UnionWith(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the given device list as a dictionary
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, EntityUid> GetDeviceList(EntityUid uid, DeviceListComponent? deviceList = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref deviceList))
|
||||||
|
return new Dictionary<string, EntityUid>();
|
||||||
|
|
||||||
|
var devices = new Dictionary<string, EntityUid>(deviceList.Devices.Count);
|
||||||
|
|
||||||
|
foreach (var deviceUid in deviceList.Devices)
|
||||||
|
{
|
||||||
|
if (!TryComp(deviceUid, out DeviceNetworkComponent? deviceNet))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
devices.Add(deviceNet.Address, deviceUid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles the given device lists connection visualisation on and off.
|
||||||
|
/// TODO: Implement an overlay that draws a line between the given entity and the entities in the device list
|
||||||
|
/// </summary>
|
||||||
|
public void ToggleVisualization(EntityUid uid, bool ensureOff = false, DeviceListComponent? deviceList = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref deviceList))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filters the broadcasts recipient list against the device list as either an allow or deny list depending on the components IsAllowList field
|
||||||
|
/// </summary>
|
||||||
|
private void OnBeforeBroadcast(EntityUid uid, DeviceListComponent component, BeforeBroadcastAttemptEvent args)
|
||||||
|
{
|
||||||
|
//Don't filter anything if the device list is empty
|
||||||
|
if (component.Devices.Count == 0)
|
||||||
|
{
|
||||||
|
if (component.IsAllowList) args.Cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<DeviceNetworkComponent> filteredRecipients = new(args.Recipients.Count);
|
||||||
|
|
||||||
|
foreach (var recipient in args.Recipients)
|
||||||
|
{
|
||||||
|
if (component.Devices.Contains(recipient.Owner) == component.IsAllowList) filteredRecipients.Add(recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.ModifiedRecipients = filteredRecipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Filters incoming packets if that is enabled <see cref="OnBeforeBroadcast"/>
|
||||||
|
/// </summary>
|
||||||
|
private void OnBeforePacketSent(EntityUid uid, DeviceListComponent component, BeforePacketSentEvent args)
|
||||||
|
{
|
||||||
|
if (component.HandleIncomingPackets && component.Devices.Contains(args.Sender) != component.IsAllowList)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
if (!Resolve(uid, ref device, false))
|
if (!Resolve(uid, ref device, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (device.Address == null)
|
if (device.Address == string.Empty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
frequency ??= device.TransmitFrequency;
|
frequency ??= device.TransmitFrequency;
|
||||||
@@ -194,7 +194,8 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
var network = GetNetwork(packet.NetId);
|
var network = GetNetwork(packet.NetId);
|
||||||
if (packet.Address == null)
|
if (packet.Address == null)
|
||||||
{
|
{
|
||||||
if (network.ListeningDevices.TryGetValue(packet.Frequency, out var devices))
|
// Broadcast to all listening devices
|
||||||
|
if (network.ListeningDevices.TryGetValue(packet.Frequency, out var devices) && CheckRecipientsList(packet, ref devices))
|
||||||
{
|
{
|
||||||
var deviceCopy = ArrayPool<DeviceNetworkComponent>.Shared.Rent(devices.Count);
|
var deviceCopy = ArrayPool<DeviceNetworkComponent>.Shared.Rent(devices.Count);
|
||||||
devices.CopyTo(deviceCopy);
|
devices.CopyTo(deviceCopy);
|
||||||
@@ -231,6 +232,30 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends the <see cref="BeforeBroadcastAttemptEvent"/> to the sending entity if the packets SendBeforeBroadcastAttemptEvent field is set to true.
|
||||||
|
/// The recipients is set to the modified recipient list.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>false if the broadcast was canceled</returns>
|
||||||
|
private bool CheckRecipientsList(DeviceNetworkPacketEvent packet, ref HashSet<DeviceNetworkComponent> recipients)
|
||||||
|
{
|
||||||
|
if (!_networks.ContainsKey(packet.NetId) || !_networks[packet.NetId].Devices.ContainsKey(packet.SenderAddress))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var sender = _networks[packet.NetId].Devices[packet.SenderAddress];
|
||||||
|
if (!sender.SendBroadcastAttemptEvent)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var beforeBroadcastAttemptEvent = new BeforeBroadcastAttemptEvent(recipients);
|
||||||
|
RaiseLocalEvent(packet.Sender, beforeBroadcastAttemptEvent);
|
||||||
|
|
||||||
|
if (beforeBroadcastAttemptEvent.Cancelled || beforeBroadcastAttemptEvent.ModifiedRecipients == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
recipients = beforeBroadcastAttemptEvent.ModifiedRecipients;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void SendToConnections(ReadOnlySpan<DeviceNetworkComponent> connections, DeviceNetworkPacketEvent packet)
|
private void SendToConnections(ReadOnlySpan<DeviceNetworkComponent> connections, DeviceNetworkPacketEvent packet)
|
||||||
{
|
{
|
||||||
var xform = Transform(packet.Sender);
|
var xform = Transform(packet.Sender);
|
||||||
@@ -278,6 +303,20 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sent to the sending entity before broadcasting network packets to recipients
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BeforeBroadcastAttemptEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly IReadOnlySet<DeviceNetworkComponent> Recipients;
|
||||||
|
public HashSet<DeviceNetworkComponent>? ModifiedRecipients;
|
||||||
|
|
||||||
|
public BeforeBroadcastAttemptEvent(IReadOnlySet<DeviceNetworkComponent> recipients)
|
||||||
|
{
|
||||||
|
Recipients = recipients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event raised when a device network packet gets sent.
|
/// Event raised when a device network packet gets sent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
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.DeviceNetwork;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Movement;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Systems;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class NetworkConfiguratorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DeviceListSystem _deviceListSystem = 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!;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
//Interaction
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, AfterInteractEvent>((uid, component, args) => OnUsed(uid, component, args.Target, args.User, args.CanReach)); //TODO: Replace with utility verb?
|
||||||
|
|
||||||
|
//Verbs
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, GetVerbsEvent<UtilityVerb>>(OnAddInteractVerb);
|
||||||
|
SubscribeLocalEvent<DeviceNetworkComponent, GetVerbsEvent<AlternativeVerb>>(OnAddAlternativeSaveDeviceVerb);
|
||||||
|
|
||||||
|
//UI
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, BoundUIClosedEvent>(OnUiClosed);
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorRemoveDeviceMessage>(OnRemoveDevice);
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorClearDevicesMessage>(OnClearDevice);
|
||||||
|
SubscribeLocalEvent<NetworkConfiguratorComponent, NetworkConfiguratorButtonPressedMessage>(OnConfigButtonPressed);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DeviceListComponent, ComponentRemove>(OnComponentRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var component in EntityManager.EntityQuery<NetworkConfiguratorComponent>())
|
||||||
|
{
|
||||||
|
if (component.ActiveDeviceList != null && EntityManager.EntityExists(component.ActiveDeviceList.Value) &&
|
||||||
|
_interactionSystem.InRangeUnobstructed(component.Owner, component.ActiveDeviceList.Value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//The network configurator is a handheld device. There can only ever be an ui session open for the player holding the device.
|
||||||
|
_uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryAddNetworkDevice(EntityUid? targetUid, EntityUid configuratorUid, EntityUid userUid,
|
||||||
|
NetworkConfiguratorComponent? configurator = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(configuratorUid, ref configurator))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryAddNetworkDevice(targetUid, userUid, configurator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryAddNetworkDevice(EntityUid? targetUid, EntityUid userUid, NetworkConfiguratorComponent configurator, DeviceNetworkComponent? device = null)
|
||||||
|
{
|
||||||
|
if (!targetUid.HasValue || !Resolve(targetUid.Value, ref device))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(device.Address))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("network-configurator-device-failed", ("device", targetUid)), Filter.Entities(userUid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configurator.Devices.ContainsValue(targetUid.Value))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("network-configurator-device-already-saved", ("device", targetUid)), Filter.Entities(userUid));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
configurator.Devices.Add(device.Address, targetUid.Value);
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("network-configurator-device-saved", ("address", device.Address), ("device", targetUid)),
|
||||||
|
Filter.Entities(userUid));
|
||||||
|
|
||||||
|
UpdateUiState(configurator.Owner, configurator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AccessCheck(EntityUid target, EntityUid? user, NetworkConfiguratorComponent component)
|
||||||
|
{
|
||||||
|
if (!TryComp(target, out AccessReaderComponent? reader) || user == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_accessSystem.IsAllowed(user.Value, reader))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SoundSystem.Play(Filter.Pvs(user.Value), component.SoundNoAccess.GetSound(), target, AudioParams.Default.WithVolume(-2f).WithPitchScale(1.2f));
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("network-configurator-device-access-denied"), target, Filter.Entities(user.Value));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnComponentRemoved(EntityUid uid, DeviceListComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
_uiSystem.GetUiOrNull(component.Owner, NetworkConfiguratorUiKey.Configure)?.CloseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Interactions
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (!canReach)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<DeviceListComponent>(target))
|
||||||
|
{
|
||||||
|
TryAddNetworkDevice(target, user, component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenDeviceListUi(target, user, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Verbs
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue || !HasComp<DeviceNetworkComponent>(args.Target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var isDeviceList = HasComp<DeviceListComponent>(args.Target);
|
||||||
|
|
||||||
|
UtilityVerb verb = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString(isDeviceList ? "network-configurator-configure" : "network-configurator-save-device"),
|
||||||
|
IconTexture = isDeviceList ? "/Textures/Interface/VerbIcons/settings.svg.192dpi.png" : "/Textures/Interface/VerbIcons/in.svg.192dpi.png",
|
||||||
|
Act = () => OnUsed(uid, component, args.Target, args.User),
|
||||||
|
Impact = LogImpact.Low
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Powerful. Funny alt interact using.
|
||||||
|
/// Adds an alternative verb for saving a device on the configurator for entities with the <see cref="DeviceListComponent"/>.
|
||||||
|
/// Allows alt clicking entities with a network configurator that would otherwise trigger a different action like entities
|
||||||
|
/// with a <see cref="DeviceListComponent"/>
|
||||||
|
/// </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))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AlternativeVerb verb = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("network-configurator-save-device"),
|
||||||
|
IconTexture = "/Textures/Interface/VerbIcons/in.svg.192dpi.png",
|
||||||
|
Act = () => TryAddNetworkDevice(args.Target, args.Using.Value, args.User),
|
||||||
|
Impact = LogImpact.Low
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UI
|
||||||
|
|
||||||
|
/// <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 (!targetUid.HasValue || !TryComp(userUid, out ActorComponent? actor) || !AccessCheck(targetUid.Value, userUid, configurator))
|
||||||
|
return;
|
||||||
|
|
||||||
|
configurator.ActiveDeviceList = targetUid;
|
||||||
|
_uiSystem.GetUiOrNull(configurator.Owner, NetworkConfiguratorUiKey.Configure)?.Open(actor.PlayerSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends the list of saved devices to the ui
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateUiState(EntityUid uid, NetworkConfiguratorComponent component)
|
||||||
|
{
|
||||||
|
HashSet<(string address, string name)> devices = new();
|
||||||
|
HashSet<string> invalidDevices = new();
|
||||||
|
|
||||||
|
foreach (var pair in component.Devices)
|
||||||
|
{
|
||||||
|
if (!Exists(pair.Value))
|
||||||
|
{
|
||||||
|
invalidDevices.Add(pair.Key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.Add((pair.Key, Name(pair.Value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove saved entities that don't exist anymore
|
||||||
|
foreach (var invalidDevice in invalidDevices)
|
||||||
|
{
|
||||||
|
component.Devices.Remove(invalidDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
_uiSystem.GetUiOrNull(uid, NetworkConfiguratorUiKey.List)?.SetState(new NetworkConfiguratorUserInterfaceState(devices));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the active device list when the ui is closed
|
||||||
|
/// </summary>
|
||||||
|
private void OnUiClosed(EntityUid uid, NetworkConfiguratorComponent component, BoundUIClosedEvent args)
|
||||||
|
{
|
||||||
|
component.ActiveDeviceList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a device from the saved devices list
|
||||||
|
/// </summary>
|
||||||
|
private void OnRemoveDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorRemoveDeviceMessage args)
|
||||||
|
{
|
||||||
|
component.Devices.Remove(args.Address);
|
||||||
|
UpdateUiState(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the saved devices
|
||||||
|
/// </summary>
|
||||||
|
private void OnClearDevice(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorClearDevicesMessage _)
|
||||||
|
{
|
||||||
|
component.Devices.Clear();
|
||||||
|
UpdateUiState(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles all the button presses from the config ui.
|
||||||
|
/// Modifies, copies or visualizes the targets device list
|
||||||
|
/// </summary>
|
||||||
|
private void OnConfigButtonPressed(EntityUid uid, NetworkConfiguratorComponent component, NetworkConfiguratorButtonPressedMessage args)
|
||||||
|
{
|
||||||
|
if (!component.ActiveDeviceList.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (args.ButtonKey)
|
||||||
|
{
|
||||||
|
case NetworkConfiguratorButtonKey.Set:
|
||||||
|
_deviceListSystem.UpdateDeviceList(component.ActiveDeviceList.Value, new HashSet<EntityUid>(component.Devices.Values));
|
||||||
|
break;
|
||||||
|
case NetworkConfiguratorButtonKey.Add:
|
||||||
|
_deviceListSystem.UpdateDeviceList(component.ActiveDeviceList.Value, new HashSet<EntityUid>(component.Devices.Values), true);
|
||||||
|
break;
|
||||||
|
case NetworkConfiguratorButtonKey.Clear:
|
||||||
|
_deviceListSystem.UpdateDeviceList(component.ActiveDeviceList.Value, new HashSet<EntityUid>());
|
||||||
|
break;
|
||||||
|
case NetworkConfiguratorButtonKey.Copy:
|
||||||
|
component.Devices = _deviceListSystem.GetDeviceList(component.ActiveDeviceList.Value);
|
||||||
|
UpdateUiState(uid, component);
|
||||||
|
break;
|
||||||
|
case NetworkConfiguratorButtonKey.Show:
|
||||||
|
_deviceListSystem.ToggleVisualization(component.ActiveDeviceList.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.DeviceNetwork;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum NetworkConfiguratorUiKey
|
||||||
|
{
|
||||||
|
List,
|
||||||
|
Configure
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum NetworkConfiguratorButtonKey
|
||||||
|
{
|
||||||
|
Set,
|
||||||
|
Add,
|
||||||
|
Edit,
|
||||||
|
Clear,
|
||||||
|
Copy,
|
||||||
|
Show
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message sent when the remove button for one device on the list was pressed
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class NetworkConfiguratorRemoveDeviceMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string Address;
|
||||||
|
|
||||||
|
public NetworkConfiguratorRemoveDeviceMessage(string address)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message sent when the clear button was pressed
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class NetworkConfiguratorClearDevicesMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class NetworkConfiguratorButtonPressedMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly NetworkConfiguratorButtonKey ButtonKey;
|
||||||
|
|
||||||
|
public NetworkConfiguratorButtonPressedMessage(NetworkConfiguratorButtonKey buttonKey)
|
||||||
|
{
|
||||||
|
ButtonKey = buttonKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.DeviceNetwork;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class NetworkConfiguratorUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public readonly HashSet<(string address, string name)> DeviceList;
|
||||||
|
|
||||||
|
public NetworkConfiguratorUserInterfaceState(HashSet<(string, string)> deviceList)
|
||||||
|
{
|
||||||
|
DeviceList = deviceList;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Resources/Locale/en-US/devices/network-configurator.ftl
Normal file
22
Resources/Locale/en-US/devices/network-configurator.ftl
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Popups
|
||||||
|
|
||||||
|
network-configurator-device-saved = Successfully saved network device {$device} with address {$address}!
|
||||||
|
network-configurator-device-failed = Failed to save network device {$device}! No address assigned!
|
||||||
|
network-configurator-device-already-saved = network device: {$device} is already saved.
|
||||||
|
network-configurator-device-access-denied = Access denied!
|
||||||
|
|
||||||
|
# Verbs
|
||||||
|
network-configurator-save-device = Save device
|
||||||
|
network-configurator-configure = Configure
|
||||||
|
|
||||||
|
# ui
|
||||||
|
network-configurator-ui-clear-button = Clear
|
||||||
|
network-configurator-ui-count-label = {$count} Devices
|
||||||
|
|
||||||
|
# tooltips
|
||||||
|
network-configurator-tooltip-set = Sets targets device list
|
||||||
|
network-configurator-tooltip-add = Adds to targets device list
|
||||||
|
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
|
||||||
@@ -181,10 +181,18 @@
|
|||||||
qualities:
|
qualities:
|
||||||
- Pulsing
|
- Pulsing
|
||||||
- type: SignalLinker
|
- type: SignalLinker
|
||||||
|
- type: NetworkConfigurator
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.NetworkConfiguratorUiKey.List
|
||||||
|
inHandsOnly: true
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.SignalLinkerUiKey.Key
|
- key: enum.SignalLinkerUiKey.Key
|
||||||
type: SignalPortSelectorBoundUserInterface
|
type: SignalPortSelectorBoundUserInterface
|
||||||
|
- key: enum.NetworkConfiguratorUiKey.List
|
||||||
|
type: NetworkConfiguratorBoundUserInterface
|
||||||
|
- key: enum.NetworkConfiguratorUiKey.Configure
|
||||||
|
type: NetworkConfiguratorBoundUserInterface
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- DroneUsable
|
- DroneUsable
|
||||||
|
|||||||
@@ -11,9 +11,8 @@
|
|||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: AtmosDevices
|
||||||
receiveFrequencyId: AtmosMonitor
|
receiveFrequencyId: AtmosMonitor
|
||||||
- type: ApcNetworkConnection
|
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Inorganic
|
damageContainer: Inorganic
|
||||||
|
|||||||
@@ -27,11 +27,10 @@
|
|||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: AtmosDevices
|
||||||
receiveFrequencyId: AtmosMonitor
|
receiveFrequencyId: AtmosMonitor
|
||||||
transmitFrequencyId: AtmosMonitor
|
transmitFrequencyId: AtmosMonitor
|
||||||
prefix: device-address-prefix-vent
|
prefix: device-address-prefix-vent
|
||||||
- type: ApcNetworkConnection
|
|
||||||
- type: AtmosAlarmable
|
- type: AtmosAlarmable
|
||||||
alarmedBy:
|
alarmedBy:
|
||||||
- AirAlarm
|
- AirAlarm
|
||||||
@@ -99,11 +98,10 @@
|
|||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: AtmosDevices
|
||||||
receiveFrequencyId: AtmosMonitor
|
receiveFrequencyId: AtmosMonitor
|
||||||
transmitFrequencyId: AtmosMonitor
|
transmitFrequencyId: AtmosMonitor
|
||||||
prefix: device-address-prefix-scrubber
|
prefix: device-address-prefix-scrubber
|
||||||
- type: ApcNetworkConnection
|
|
||||||
- type: AtmosAlarmable
|
- type: AtmosAlarmable
|
||||||
alarmedBy:
|
alarmedBy:
|
||||||
- AirAlarm
|
- AirAlarm
|
||||||
|
|||||||
@@ -10,10 +10,12 @@
|
|||||||
usesApcPower: true
|
usesApcPower: true
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: AtmosDevices
|
||||||
receiveFrequencyId: AtmosMonitor
|
receiveFrequencyId: AtmosMonitor
|
||||||
transmitFrequencyId: AtmosMonitor
|
transmitFrequencyId: AtmosMonitor
|
||||||
- type: ApcNetworkConnection
|
sendBroadcastAttemptEvent: true
|
||||||
|
- type: WiredNetworkConnection
|
||||||
|
- type: DeviceList
|
||||||
- type: AtmosMonitor
|
- type: AtmosMonitor
|
||||||
temperatureThreshold: stationTemperature
|
temperatureThreshold: stationTemperature
|
||||||
pressureThreshold: stationPressure
|
pressureThreshold: stationPressure
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
usesApcPower: true
|
usesApcPower: true
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: AtmosDevices
|
||||||
receiveFrequencyId: AtmosMonitor
|
receiveFrequencyId: AtmosMonitor
|
||||||
transmitFrequencyId: AtmosMonitor
|
transmitFrequencyId: AtmosMonitor
|
||||||
- type: ApcNetworkConnection
|
- type: ApcNetworkConnection
|
||||||
|
|||||||
Reference in New Issue
Block a user