Add fax admin panel (#15215)

This commit is contained in:
Morb
2023-04-16 23:20:57 -07:00
committed by GitHub
parent 55f82d2c15
commit 0e6b273f1f
13 changed files with 367 additions and 27 deletions

View File

@@ -15,6 +15,7 @@
<cc:CommandButton Command="announceui" Text="{Loc admin-player-actions-window-announce}"/> <cc:CommandButton Command="announceui" Text="{Loc admin-player-actions-window-announce}"/>
<cc:UICommandButton Command="callshuttle" Text="{Loc admin-player-actions-window-shuttle}" WindowType="{x:Type at:AdminShuttleWindow}"/> <cc:UICommandButton Command="callshuttle" Text="{Loc admin-player-actions-window-shuttle}" WindowType="{x:Type at:AdminShuttleWindow}"/>
<cc:CommandButton Command="adminlogs" Text="{Loc admin-player-actions-window-admin-logs}"/> <cc:CommandButton Command="adminlogs" Text="{Loc admin-player-actions-window-admin-logs}"/>
<cc:CommandButton Command="faxui" Text="{Loc admin-player-actions-window-admin-fax}"/>
</GridContainer> </GridContainer>
</BoxContainer> </BoxContainer>
</Control> </Control>

View File

@@ -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);
}
}

View File

@@ -0,0 +1,27 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc admin-fax-title}"
MinWidth="400">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc admin-fax-fax}" />
<Control MinWidth="4" />
<OptionButton Name="FaxSelector" HorizontalExpand="True" />
<Control MinWidth="4" />
<Button Name="FollowButton" Text="{Loc admin-fax-follow}" />
</BoxContainer>
<Control MinHeight="5" />
<LineEdit Name="TitleEdit" HorizontalExpand="True" PlaceHolder="{Loc admin-fax-title-placeholder}"></LineEdit>
<Control MinHeight="5" />
<LineEdit Name="FromEdit" HorizontalExpand="True" PlaceHolder="{Loc admin-fax-from-placeholder}"></LineEdit>
<Control MinHeight="5" />
<TextEdit Name="MessageEdit" HorizontalExpand="True" MinHeight="200"></TextEdit>
<Control MinHeight="5" />
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc admin-fax-stamp}" />
<Control MinWidth="5" />
<OptionButton Name="StampSelector" HorizontalExpand="True" />
</BoxContainer>
<Control MinHeight="10" />
<Button Name="SendButton" Text="{Loc admin-fax-send}"></Button>
</BoxContainer>
</DefaultWindow>

View File

@@ -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<EntityUid>? 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<ILocalizationManager>();
MessageEdit.Placeholder = new Rope.Leaf(loc.GetString("admin-fax-message-placeholder")); // TextEdit work only with Nodes
}
public void PopulateFaxes(List<AdminFaxEntry> 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<IResourceCache>().GetResource<RSIResource>(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));
}
}

View File

@@ -1,12 +1,14 @@
using Content.Shared.Fax; using Content.Shared.Fax;
using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Fax.UI; namespace Content.Client.Fax.UI;
[UsedImplicitly]
public sealed class FaxBoundUi : BoundUserInterface public sealed class FaxBoundUi : BoundUserInterface
{ {
private FaxWindow? _window; private FaxWindow? _window;
public FaxBoundUi(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey) public FaxBoundUi(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{ {
} }
@@ -42,7 +44,7 @@ public sealed class FaxBoundUi : BoundUserInterface
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
{ {
base.UpdateState(state); base.UpdateState(state);
if (_window == null || state is not FaxUiState cast) if (_window == null || state is not FaxUiState cast)
return; return;

View File

@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using Content.Shared.Fax; using Content.Shared.Fax;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;

View File

@@ -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<EuiManager>();
var ui = new AdminFaxEui();
eui.OpenEui(ui, player);
}
}

View File

@@ -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<IEntitySystemManager>().GetEntitySystem<FaxSystem>();
_followerSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<FollowerSystem>();
}
public override void Opened()
{
StateDirty();
}
public override AdminFaxEuiState GetNewState()
{
var faxes = _entityManager.EntityQueryEnumerator<FaxMachineComponent, DeviceNetworkComponent>();
var entries = new List<AdminFaxEntry>();
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<GhostComponent>(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;
}
}
}
}

View File

@@ -142,7 +142,7 @@ public sealed class FaxPrintout
{ {
} }
public FaxPrintout(string content, string name, string? prototypeId, string? stampState = null, List<string>? stampedBy = null) public FaxPrintout(string content, string name, string? prototypeId = null, string? stampState = null, List<string>? stampedBy = null)
{ {
Content = content; Content = content;
Name = name; Name = name;

View File

@@ -7,7 +7,6 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.Paper; using Content.Server.Paper;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Tools; using Content.Server.Tools;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
@@ -18,6 +17,7 @@ using Content.Shared.Emag.Systems;
using Content.Shared.Fax; using Content.Shared.Fax;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -38,7 +38,7 @@ public sealed class FaxSystem : EntitySystem
[Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
public const string PaperSlotId = "Paper"; private const string PaperSlotId = "Paper";
public override void Initialize() public override void Initialize()
{ {
@@ -69,29 +69,30 @@ public sealed class FaxSystem : EntitySystem
{ {
base.Update(frameTime); base.Update(frameTime);
foreach (var (comp, receiver) in EntityQuery<FaxMachineComponent, ApcPowerReceiverComponent>()) var query = EntityQueryEnumerator<FaxMachineComponent, ApcPowerReceiverComponent>();
while (query.MoveNext(out var uid, out var fax, out var receiver))
{ {
if (!receiver.Powered) if (!receiver.Powered)
continue; continue;
ProcessPrintingAnimation(frameTime, comp); ProcessPrintingAnimation(uid, frameTime, fax);
ProcessInsertingAnimation(frameTime, comp); ProcessInsertingAnimation(uid, frameTime, fax);
ProcessSendingTimeout(frameTime, comp); ProcessSendingTimeout(uid, frameTime, fax);
} }
} }
private void ProcessPrintingAnimation(float frameTime, FaxMachineComponent comp) private void ProcessPrintingAnimation(EntityUid uid, float frameTime, FaxMachineComponent comp)
{ {
if (comp.PrintingTimeRemaining > 0) if (comp.PrintingTimeRemaining > 0)
{ {
comp.PrintingTimeRemaining -= frameTime; comp.PrintingTimeRemaining -= frameTime;
UpdateAppearance(comp.Owner, comp); UpdateAppearance(uid, comp);
var isAnimationEnd = comp.PrintingTimeRemaining <= 0; var isAnimationEnd = comp.PrintingTimeRemaining <= 0;
if (isAnimationEnd) if (isAnimationEnd)
{ {
SpawnPaperFromQueue(comp.Owner, comp); SpawnPaperFromQueue(uid, comp);
UpdateUserInterface(comp.Owner, comp); UpdateUserInterface(uid, comp);
} }
return; return;
@@ -100,40 +101,40 @@ public sealed class FaxSystem : EntitySystem
if (comp.PrintingQueue.Count > 0) if (comp.PrintingQueue.Count > 0)
{ {
comp.PrintingTimeRemaining = comp.PrintingTime; 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) if (comp.InsertingTimeRemaining <= 0)
return; return;
comp.InsertingTimeRemaining -= frameTime; comp.InsertingTimeRemaining -= frameTime;
UpdateAppearance(comp.Owner, comp); UpdateAppearance(uid, comp);
var isAnimationEnd = comp.InsertingTimeRemaining <= 0; var isAnimationEnd = comp.InsertingTimeRemaining <= 0;
if (isAnimationEnd) if (isAnimationEnd)
{ {
_itemSlotsSystem.SetLock(comp.Owner, comp.PaperSlot, false); _itemSlotsSystem.SetLock(uid, comp.PaperSlot, false);
UpdateUserInterface(comp.Owner, comp); UpdateUserInterface(uid, comp);
} }
} }
private void ProcessSendingTimeout(float frameTime, FaxMachineComponent comp) private void ProcessSendingTimeout(EntityUid uid, float frameTime, FaxMachineComponent comp)
{ {
if (comp.SendTimeoutRemaining > 0) if (comp.SendTimeoutRemaining > 0)
{ {
comp.SendTimeoutRemaining -= frameTime; comp.SendTimeoutRemaining -= frameTime;
if (comp.SendTimeoutRemaining <= 0) if (comp.SendTimeoutRemaining <= 0)
UpdateUserInterface(comp.Owner, comp); UpdateUserInterface(uid, comp);
} }
} }
private void OnComponentInit(EntityUid uid, FaxMachineComponent component, ComponentInit args) 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); UpdateAppearance(uid, component);
} }
@@ -185,7 +186,7 @@ public sealed class FaxSystem : EntitySystem
} }
if (isInsertInterrupted || isPrintInterrupted) if (isInsertInterrupted || isPrintInterrupted)
UpdateAppearance(component.Owner, component); UpdateAppearance(uid, component);
_itemSlotsSystem.SetLock(uid, component.PaperSlot, !args.Powered); // Lock slot when power is off _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 /// Accepts a new message and adds it to the queue to print
/// If has parameter "notifyAdmins" also output a special message to admin chat. /// If has parameter "notifyAdmins" also output a special message to admin chat.
/// </summary> /// </summary>
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)) if (!Resolve(uid, ref component))
return; return;
var faxName = Loc.GetString("fax-machine-popup-source-unknown"); var faxName = Loc.GetString("fax-machine-popup-source-unknown");
if (fromAddress != null && component.KnownFaxes.ContainsKey(fromAddress)) // If message received from unknown for fax address if (fromAddress != null && component.KnownFaxes.TryGetValue(fromAddress, out var fax)) // If message received from unknown fax address
faxName = component.KnownFaxes[fromAddress]; faxName = fax;
_popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-received", ("from", faxName)), uid); _popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-received", ("from", faxName)), uid);
_appearanceSystem.SetData(uid, FaxMachineVisuals.VisualState, FaxMachineVisualState.Printing); _appearanceSystem.SetData(uid, FaxMachineVisuals.VisualState, FaxMachineVisualState.Printing);
@@ -476,6 +477,6 @@ public sealed class FaxSystem : EntitySystem
private void NotifyAdmins(string faxName) private void NotifyAdmins(string faxName)
{ {
_chat.SendAdminAnnouncement(Loc.GetString("fax-machine-chat-notify", ("fax", 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));
} }
} }

View File

@@ -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<AdminFaxEntry> Entries { get; }
public AdminFaxEuiState(List<AdminFaxEntry> 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;
}
}
}

View File

@@ -7,3 +7,4 @@ admin-player-actions-window-announce = Announce
admin-player-actions-window-shuttle = (Re)call Shuttle admin-player-actions-window-shuttle = (Re)call Shuttle
admin-player-actions-window-admin-logs = Admin Logs admin-player-actions-window-admin-logs = Admin Logs
admin-player-actions-window-admin-notes = Admin Notes admin-player-actions-window-admin-notes = Admin Notes
admin-player-actions-window-admin-fax = Admin Fax

View File

@@ -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