From: Morb <14136326+Morb0@users.noreply.github.com> Date: Mon, 17 Apr 2023 06:20:57 +0000 (-0700) Subject: Add fax admin panel (#15215) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=0e6b273f1f913b8540e0e4d2ae6efc87f946aec2;p=space-station-14.git Add fax admin panel (#15215) --- 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