From 0e6b273f1f913b8540e0e4d2ae6efc87f946aec2 Mon Sep 17 00:00:00 2001
From: Morb <14136326+Morb0@users.noreply.github.com>
Date: Sun, 16 Apr 2023 23:20:57 -0700
Subject: [PATCH] Add fax admin panel (#15215)
---
.../UI/Tabs/AdminTab/AdminTab.xaml | 1 +
Content.Client/Fax/AdminUI/AdminFaxEui.cs | 37 ++++++++
.../Fax/AdminUI/AdminFaxWindow.xaml | 27 ++++++
.../Fax/AdminUI/AdminFaxWindow.xaml.cs | 95 +++++++++++++++++++
Content.Client/Fax/UI/FaxBoundUi.cs | 6 +-
Content.Client/Fax/UI/FaxWindow.xaml.cs | 1 -
.../Administration/Commands/FaxUiCommand.cs | 31 ++++++
Content.Server/Fax/AdminUI/AdminFaxEui.cs | 65 +++++++++++++
Content.Server/Fax/FaxMachineComponent.cs | 2 +-
Content.Server/Fax/FaxSystem.cs | 47 ++++-----
Content.Shared/Fax/AdminFaxEui.cs | 68 +++++++++++++
.../tabs/admin-tab/player-actions-window.ftl | 1 +
Resources/Locale/en-US/fax/fax-admin.ftl | 13 +++
13 files changed, 367 insertions(+), 27 deletions(-)
create mode 100644 Content.Client/Fax/AdminUI/AdminFaxEui.cs
create mode 100644 Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
create mode 100644 Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
create mode 100644 Content.Server/Administration/Commands/FaxUiCommand.cs
create mode 100644 Content.Server/Fax/AdminUI/AdminFaxEui.cs
create mode 100644 Content.Shared/Fax/AdminFaxEui.cs
create mode 100644 Resources/Locale/en-US/fax/fax-admin.ftl
diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
index 2573a2cf69..547e8d01a9 100644
--- a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
+++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml
@@ -15,6 +15,7 @@
+
diff --git a/Content.Client/Fax/AdminUI/AdminFaxEui.cs b/Content.Client/Fax/AdminUI/AdminFaxEui.cs
new file mode 100644
index 0000000000..06d0f71d27
--- /dev/null
+++ b/Content.Client/Fax/AdminUI/AdminFaxEui.cs
@@ -0,0 +1,37 @@
+using Content.Client.Eui;
+using Content.Shared.Eui;
+using Content.Shared.Fax;
+using JetBrains.Annotations;
+
+namespace Content.Client.Fax.AdminUI;
+
+[UsedImplicitly]
+public sealed class AdminFaxEui : BaseEui
+{
+ private readonly AdminFaxWindow _window;
+
+ public AdminFaxEui()
+ {
+ _window = new AdminFaxWindow();
+ _window.OnClose += () => SendMessage(new AdminFaxEuiMsg.Close());
+ _window.OnFollowFax += uid => SendMessage(new AdminFaxEuiMsg.Follow(uid));
+ _window.OnMessageSend += args => SendMessage(new AdminFaxEuiMsg.Send(args.uid, args.title, args.from, args.message, args.stamp));
+ }
+
+ public override void Opened()
+ {
+ _window.OpenCentered();
+ }
+
+ public override void Closed()
+ {
+ _window.Close();
+ }
+
+ public override void HandleState(EuiStateBase state)
+ {
+ if (state is not AdminFaxEuiState cast)
+ return;
+ _window.PopulateFaxes(cast.Entries);
+ }
+}
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
new file mode 100644
index 0000000000..0c148a9fdc
--- /dev/null
+++ b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
new file mode 100644
index 0000000000..207a39c213
--- /dev/null
+++ b/Content.Client/Fax/AdminUI/AdminFaxWindow.xaml.cs
@@ -0,0 +1,95 @@
+using Content.Shared.Fax;
+using Robust.Client.AutoGenerated;
+using Robust.Client.ResourceManagement;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Fax.AdminUI;
+
+[GenerateTypedNameReferences]
+public sealed partial class AdminFaxWindow : DefaultWindow
+{
+ private const string StampsRsiPath = "/Textures/Objects/Misc/bureaucracy.rsi";
+
+ public Action<(EntityUid uid, string title, string from, string message, string stamp)>? OnMessageSend;
+ public Action? OnFollowFax;
+
+ public AdminFaxWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+
+ PopulateStamps();
+
+ FaxSelector.OnItemSelected += args => FaxSelector.SelectId(args.Id);
+ StampSelector.OnItemSelected += args => StampSelector.SelectId(args.Id);
+ FollowButton.OnPressed += FollowFax;
+ SendButton.OnPressed += SendMessage;
+
+ var loc = IoCManager.Resolve();
+ MessageEdit.Placeholder = new Rope.Leaf(loc.GetString("admin-fax-message-placeholder")); // TextEdit work only with Nodes
+ }
+
+ public void PopulateFaxes(List faxes)
+ {
+ for (var i = 0; i < faxes.Count; i++)
+ {
+ var fax = faxes[i];
+ FaxSelector.AddItem($"{fax.Name} ({fax.Address})", i);
+ FaxSelector.SetItemMetadata(i, fax.Uid);
+ }
+ }
+
+ private void PopulateStamps()
+ {
+ var rsi = IoCManager.Resolve().GetResource(StampsRsiPath).RSI;
+ using (var enumerator = rsi.GetEnumerator())
+ {
+ var i = 0;
+ while (enumerator.MoveNext())
+ {
+ var state = enumerator.Current;
+ var stateName = state.StateId.Name!;
+ if (!stateName.StartsWith("paper_stamp-"))
+ continue;
+
+ StampSelector.AddItem(stateName, i);
+ StampSelector.SetItemMetadata(i, stateName);
+ i++;
+ }
+ }
+ }
+
+ private void FollowFax(BaseButton.ButtonEventArgs obj)
+ {
+ var faxUid = (EntityUid?) FaxSelector.SelectedMetadata;
+ if (faxUid == null)
+ return;
+
+ OnFollowFax?.Invoke(faxUid.Value);
+ }
+
+ private void SendMessage(BaseButton.ButtonEventArgs obj)
+ {
+ var faxUid = (EntityUid?) FaxSelector.SelectedMetadata;
+ if (faxUid == null)
+ return;
+
+ var stamp = (string?) StampSelector.SelectedMetadata;
+ if (stamp == null)
+ return;
+
+ var title = TitleEdit.Text;
+ if (string.IsNullOrEmpty(title))
+ return;
+
+ var message = Rope.Collapse(MessageEdit.TextRope);
+ if (string.IsNullOrEmpty(message))
+ return;
+
+ var from = FromEdit.Text;
+ OnMessageSend?.Invoke((faxUid.Value, title, from, message, stamp));
+ }
+}
diff --git a/Content.Client/Fax/UI/FaxBoundUi.cs b/Content.Client/Fax/UI/FaxBoundUi.cs
index f6d31e78db..19a48a3a34 100644
--- a/Content.Client/Fax/UI/FaxBoundUi.cs
+++ b/Content.Client/Fax/UI/FaxBoundUi.cs
@@ -1,12 +1,14 @@
using Content.Shared.Fax;
+using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Fax.UI;
+[UsedImplicitly]
public sealed class FaxBoundUi : BoundUserInterface
{
private FaxWindow? _window;
-
+
public FaxBoundUi(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{
}
@@ -42,7 +44,7 @@ public sealed class FaxBoundUi : BoundUserInterface
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
-
+
if (_window == null || state is not FaxUiState cast)
return;
diff --git a/Content.Client/Fax/UI/FaxWindow.xaml.cs b/Content.Client/Fax/UI/FaxWindow.xaml.cs
index 56c53d162c..759a8865ec 100644
--- a/Content.Client/Fax/UI/FaxWindow.xaml.cs
+++ b/Content.Client/Fax/UI/FaxWindow.xaml.cs
@@ -1,7 +1,6 @@
using System.Linq;
using Content.Shared.Fax;
using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
diff --git a/Content.Server/Administration/Commands/FaxUiCommand.cs b/Content.Server/Administration/Commands/FaxUiCommand.cs
new file mode 100644
index 0000000000..b06d36d08b
--- /dev/null
+++ b/Content.Server/Administration/Commands/FaxUiCommand.cs
@@ -0,0 +1,31 @@
+using Content.Server.EUI;
+using Content.Server.Fax.AdminUI;
+using Content.Shared.Administration;
+using Robust.Server.Player;
+using Robust.Shared.Console;
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class FaxUiCommand : IConsoleCommand
+{
+ public string Command => "faxui";
+
+ public string Description => Loc.GetString("cmd-faxui-desc");
+ public string Help => Loc.GetString("cmd-faxui-help");
+
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ var player = shell.Player as IPlayerSession;
+ if (player == null)
+ {
+ shell.WriteLine("shell-only-players-can-run-this-command");
+ return;
+ }
+
+ var eui = IoCManager.Resolve();
+ var ui = new AdminFaxEui();
+ eui.OpenEui(ui, player);
+ }
+}
+
diff --git a/Content.Server/Fax/AdminUI/AdminFaxEui.cs b/Content.Server/Fax/AdminUI/AdminFaxEui.cs
new file mode 100644
index 0000000000..87dd44a28d
--- /dev/null
+++ b/Content.Server/Fax/AdminUI/AdminFaxEui.cs
@@ -0,0 +1,65 @@
+using Content.Server.DeviceNetwork.Components;
+using Content.Server.EUI;
+using Content.Server.Ghost.Components;
+using Content.Shared.Eui;
+using Content.Shared.Fax;
+using Content.Shared.Follower;
+
+namespace Content.Server.Fax.AdminUI;
+
+public sealed class AdminFaxEui : BaseEui
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ private readonly FaxSystem _faxSystem;
+ private readonly FollowerSystem _followerSystem;
+
+ public AdminFaxEui()
+ {
+ IoCManager.InjectDependencies(this);
+ _faxSystem = IoCManager.Resolve().GetEntitySystem();
+ _followerSystem = IoCManager.Resolve().GetEntitySystem();
+ }
+
+ public override void Opened()
+ {
+ StateDirty();
+ }
+
+ public override AdminFaxEuiState GetNewState()
+ {
+ var faxes = _entityManager.EntityQueryEnumerator();
+ var entries = new List();
+ while (faxes.MoveNext(out var uid, out var fax, out var device))
+ {
+ entries.Add(new AdminFaxEntry(uid, fax.FaxName, device.Address));
+ }
+ return new AdminFaxEuiState(entries);
+ }
+
+ public override void HandleMessage(EuiMessageBase msg)
+ {
+ switch (msg)
+ {
+ case AdminFaxEuiMsg.Close:
+ {
+ Close();
+ break;
+ }
+ case AdminFaxEuiMsg.Follow followData:
+ {
+ if (Player.AttachedEntity == null ||
+ !_entityManager.HasComponent(Player.AttachedEntity.Value))
+ return;
+
+ _followerSystem.StartFollowingEntity(Player.AttachedEntity.Value, followData.TargetFax);
+ break;
+ }
+ case AdminFaxEuiMsg.Send sendData:
+ {
+ var printout = new FaxPrintout(sendData.Content, sendData.Title, null, sendData.StampState, new() { sendData.From });
+ _faxSystem.Receive(sendData.Target, printout);
+ break;
+ }
+ }
+ }
+}
diff --git a/Content.Server/Fax/FaxMachineComponent.cs b/Content.Server/Fax/FaxMachineComponent.cs
index edbc757650..bdc97bc245 100644
--- a/Content.Server/Fax/FaxMachineComponent.cs
+++ b/Content.Server/Fax/FaxMachineComponent.cs
@@ -142,7 +142,7 @@ public sealed class FaxPrintout
{
}
- public FaxPrintout(string content, string name, string? prototypeId, string? stampState = null, List? stampedBy = null)
+ public FaxPrintout(string content, string name, string? prototypeId = null, string? stampState = null, List? stampedBy = null)
{
Content = content;
Name = name;
diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs
index 2881dae022..26df47ce1b 100644
--- a/Content.Server/Fax/FaxSystem.cs
+++ b/Content.Server/Fax/FaxSystem.cs
@@ -7,7 +7,6 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.Paper;
using Content.Server.Popups;
using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
using Content.Server.Tools;
using Content.Server.UserInterface;
using Content.Shared.Administration.Logs;
@@ -18,6 +17,7 @@ using Content.Shared.Emag.Systems;
using Content.Shared.Fax;
using Content.Shared.Interaction;
using Robust.Server.GameObjects;
+using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
@@ -38,7 +38,7 @@ public sealed class FaxSystem : EntitySystem
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
- public const string PaperSlotId = "Paper";
+ private const string PaperSlotId = "Paper";
public override void Initialize()
{
@@ -69,29 +69,30 @@ public sealed class FaxSystem : EntitySystem
{
base.Update(frameTime);
- foreach (var (comp, receiver) in EntityQuery())
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var fax, out var receiver))
{
if (!receiver.Powered)
continue;
- ProcessPrintingAnimation(frameTime, comp);
- ProcessInsertingAnimation(frameTime, comp);
- ProcessSendingTimeout(frameTime, comp);
+ ProcessPrintingAnimation(uid, frameTime, fax);
+ ProcessInsertingAnimation(uid, frameTime, fax);
+ ProcessSendingTimeout(uid, frameTime, fax);
}
}
- private void ProcessPrintingAnimation(float frameTime, FaxMachineComponent comp)
+ private void ProcessPrintingAnimation(EntityUid uid, float frameTime, FaxMachineComponent comp)
{
if (comp.PrintingTimeRemaining > 0)
{
comp.PrintingTimeRemaining -= frameTime;
- UpdateAppearance(comp.Owner, comp);
+ UpdateAppearance(uid, comp);
var isAnimationEnd = comp.PrintingTimeRemaining <= 0;
if (isAnimationEnd)
{
- SpawnPaperFromQueue(comp.Owner, comp);
- UpdateUserInterface(comp.Owner, comp);
+ SpawnPaperFromQueue(uid, comp);
+ UpdateUserInterface(uid, comp);
}
return;
@@ -100,40 +101,40 @@ public sealed class FaxSystem : EntitySystem
if (comp.PrintingQueue.Count > 0)
{
comp.PrintingTimeRemaining = comp.PrintingTime;
- _audioSystem.PlayPvs(comp.PrintSound, comp.Owner);
+ _audioSystem.PlayPvs(comp.PrintSound, uid);
}
}
- private void ProcessInsertingAnimation(float frameTime, FaxMachineComponent comp)
+ private void ProcessInsertingAnimation(EntityUid uid, float frameTime, FaxMachineComponent comp)
{
if (comp.InsertingTimeRemaining <= 0)
return;
comp.InsertingTimeRemaining -= frameTime;
- UpdateAppearance(comp.Owner, comp);
+ UpdateAppearance(uid, comp);
var isAnimationEnd = comp.InsertingTimeRemaining <= 0;
if (isAnimationEnd)
{
- _itemSlotsSystem.SetLock(comp.Owner, comp.PaperSlot, false);
- UpdateUserInterface(comp.Owner, comp);
+ _itemSlotsSystem.SetLock(uid, comp.PaperSlot, false);
+ UpdateUserInterface(uid, comp);
}
}
- private void ProcessSendingTimeout(float frameTime, FaxMachineComponent comp)
+ private void ProcessSendingTimeout(EntityUid uid, float frameTime, FaxMachineComponent comp)
{
if (comp.SendTimeoutRemaining > 0)
{
comp.SendTimeoutRemaining -= frameTime;
if (comp.SendTimeoutRemaining <= 0)
- UpdateUserInterface(comp.Owner, comp);
+ UpdateUserInterface(uid, comp);
}
}
private void OnComponentInit(EntityUid uid, FaxMachineComponent component, ComponentInit args)
{
- _itemSlotsSystem.AddItemSlot(uid, FaxSystem.PaperSlotId, component.PaperSlot);
+ _itemSlotsSystem.AddItemSlot(uid, PaperSlotId, component.PaperSlot);
UpdateAppearance(uid, component);
}
@@ -185,7 +186,7 @@ public sealed class FaxSystem : EntitySystem
}
if (isInsertInterrupted || isPrintInterrupted)
- UpdateAppearance(component.Owner, component);
+ UpdateAppearance(uid, component);
_itemSlotsSystem.SetLock(uid, component.PaperSlot, !args.Powered); // Lock slot when power is off
}
@@ -425,14 +426,14 @@ public sealed class FaxSystem : EntitySystem
/// Accepts a new message and adds it to the queue to print
/// If has parameter "notifyAdmins" also output a special message to admin chat.
///
- public void Receive(EntityUid uid, FaxPrintout printout, string? fromAddress, FaxMachineComponent? component = null)
+ public void Receive(EntityUid uid, FaxPrintout printout, string? fromAddress = null, FaxMachineComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
var faxName = Loc.GetString("fax-machine-popup-source-unknown");
- if (fromAddress != null && component.KnownFaxes.ContainsKey(fromAddress)) // If message received from unknown for fax address
- faxName = component.KnownFaxes[fromAddress];
+ if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address
+ faxName = fax;
_popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-received", ("from", faxName)), uid);
_appearanceSystem.SetData(uid, FaxMachineVisuals.VisualState, FaxMachineVisualState.Printing);
@@ -476,6 +477,6 @@ public sealed class FaxSystem : EntitySystem
private void NotifyAdmins(string faxName)
{
_chat.SendAdminAnnouncement(Loc.GetString("fax-machine-chat-notify", ("fax", faxName)));
- _audioSystem.PlayGlobal("/Audio/Machines/high_tech_confirm.ogg", Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false);
+ _audioSystem.PlayGlobal("/Audio/Machines/high_tech_confirm.ogg", Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false, AudioParams.Default.WithVolume(-8f));
}
}
diff --git a/Content.Shared/Fax/AdminFaxEui.cs b/Content.Shared/Fax/AdminFaxEui.cs
new file mode 100644
index 0000000000..cd59b4c38d
--- /dev/null
+++ b/Content.Shared/Fax/AdminFaxEui.cs
@@ -0,0 +1,68 @@
+using Content.Shared.Eui;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Fax;
+
+[Serializable, NetSerializable]
+public sealed class AdminFaxEuiState : EuiStateBase
+{
+ public List Entries { get; }
+
+ public AdminFaxEuiState(List entries)
+ {
+ Entries = entries;
+ }
+}
+
+[Serializable, NetSerializable]
+public sealed class AdminFaxEntry
+{
+ public EntityUid Uid { get; }
+ public string Name { get; }
+ public string Address { get; }
+
+ public AdminFaxEntry(EntityUid uid, string name, string address)
+ {
+ Uid = uid;
+ Name = name;
+ Address = address;
+ }
+}
+
+public static class AdminFaxEuiMsg
+{
+ [Serializable, NetSerializable]
+ public sealed class Close : EuiMessageBase
+ {
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class Follow : EuiMessageBase
+ {
+ public EntityUid TargetFax { get; }
+
+ public Follow(EntityUid targetFax)
+ {
+ TargetFax = targetFax;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class Send : EuiMessageBase
+ {
+ public EntityUid Target { get; }
+ public string Title { get; }
+ public string From { get; }
+ public string Content { get; }
+ public string StampState { get; }
+
+ public Send(EntityUid target, string title, string from, string content, string stamp)
+ {
+ Target = target;
+ Title = title;
+ From = from;
+ Content = content;
+ StampState = stamp;
+ }
+ }
+}
diff --git a/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl b/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
index a03677a4b0..57ef3e04b8 100644
--- a/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
+++ b/Resources/Locale/en-US/administration/ui/tabs/admin-tab/player-actions-window.ftl
@@ -7,3 +7,4 @@ admin-player-actions-window-announce = Announce
admin-player-actions-window-shuttle = (Re)call Shuttle
admin-player-actions-window-admin-logs = Admin Logs
admin-player-actions-window-admin-notes = Admin Notes
+admin-player-actions-window-admin-fax = Admin Fax
diff --git a/Resources/Locale/en-US/fax/fax-admin.ftl b/Resources/Locale/en-US/fax/fax-admin.ftl
new file mode 100644
index 0000000000..c05b8338fc
--- /dev/null
+++ b/Resources/Locale/en-US/fax/fax-admin.ftl
@@ -0,0 +1,13 @@
+# Command
+cmd-faxui-desc = Open admin window for sending faxes
+cmd-faxui-help = Usage: faxui
+
+# Window
+admin-fax-title = Admin Fax Manager
+admin-fax-fax = Fax:
+admin-fax-follow = Follow
+admin-fax-title-placeholder = Paper name...
+admin-fax-from-placeholder = From who...
+admin-fax-message-placeholder = Your message here...
+admin-fax-stamp = Stamp:
+admin-fax-send = Send
--
2.51.2