diff --git a/Content.Client/Administration/BwoinkSystem.cs b/Content.Client/Administration/BwoinkSystem.cs new file mode 100644 index 0000000000..b68ea191da --- /dev/null +++ b/Content.Client/Administration/BwoinkSystem.cs @@ -0,0 +1,77 @@ +#nullable enable +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Content.Client.Administration.UI; +using Content.Shared.Administration; +using JetBrains.Annotations; +using Robust.Client.Player; +using Robust.Shared.Localization; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Network; +using Robust.Shared.Players; +using Robust.Shared.Player; +using Robust.Shared.Audio; +using Robust.Shared.IoC; + +namespace Content.Client.Administration +{ + [UsedImplicitly] + public class BwoinkSystem : SharedBwoinkSystem + { + [Dependency] private readonly IPlayerManager _playerManager = default!; + private readonly Dictionary _activeWindowMap = new(); + + protected override void OnBwoinkTextMessage(BwoinkTextMessage message, EntitySessionEventArgs eventArgs) + { + base.OnBwoinkTextMessage(message, eventArgs); + LogBwoink(message); + // Actual line + var window = EnsureWindow(message.ChannelId); + window.ReceiveLine(message.Text); + // Play a sound if we didn't send it + var localPlayer = _playerManager.LocalPlayer; + if (localPlayer?.UserId != message.TrueSender) + { + SoundSystem.Play(Filter.Local(), "/Audio/Effects/adminhelp.ogg"); + } + } + + public BwoinkWindow EnsureWindow(NetUserId channelId) + { + if (_activeWindowMap.TryGetValue(channelId, out var existingWindow)) + { + existingWindow.Open(); + return existingWindow; + } + string title; + if (_playerManager.SessionsDict.TryGetValue(channelId, out var otherSession)) + { + title = otherSession.Name; + } + else + { + title = channelId.ToString(); + } + var window = new BwoinkWindow(channelId, title); + _activeWindowMap[channelId] = window; + window.Open(); + return window; + } + + public void EnsureWindowForLocalPlayer() + { + var localPlayer = _playerManager.LocalPlayer; + if (localPlayer != null) + EnsureWindow(localPlayer.UserId); + } + + public void Send(NetUserId channelId, string text) + { + // Reuse the channel ID as the 'true sender'. + // Server will ignore this and if someone makes it not ignore this (which is bad, allows impersonation!!!), that will help. + RaiseNetworkEvent(new BwoinkTextMessage(channelId, channelId, text)); + } + } +} + diff --git a/Content.Client/Administration/UI/BwoinkWindow.xaml b/Content.Client/Administration/UI/BwoinkWindow.xaml new file mode 100644 index 0000000000..f07e6b672a --- /dev/null +++ b/Content.Client/Administration/UI/BwoinkWindow.xaml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Content.Client/Administration/UI/BwoinkWindow.xaml.cs b/Content.Client/Administration/UI/BwoinkWindow.xaml.cs new file mode 100644 index 0000000000..02743d4b7b --- /dev/null +++ b/Content.Client/Administration/UI/BwoinkWindow.xaml.cs @@ -0,0 +1,65 @@ +#nullable enable +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Content.Client.UserInterface; +using Content.Client.Administration; +using Content.Shared; +using Robust.Client.Credits; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Utility; +using Robust.Shared.Network; +using Robust.Shared.GameObjects; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.Administration.UI +{ + /// + /// This window connects to a BwoinkSystem channel. BwoinkSystem manages the rest. + /// + [GenerateTypedNameReferences] + public partial class BwoinkWindow : SS14Window + { + [Dependency] private readonly IEntitySystemManager _systemManager = default!; + + private readonly NetUserId _channelId; + + public BwoinkWindow(NetUserId channelId, string title) + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + Title = title; + _channelId = channelId; + + SenderLineEdit.OnTextEntered += Input_OnTextEntered; + + MinSize = (650, 450); + } + + private void Input_OnTextEntered(LineEdit.LineEditEventArgs args) + { + if (!string.IsNullOrWhiteSpace(args.Text)) + { + var bwoink = _systemManager.GetEntitySystem(); + bwoink.Send(_channelId, args.Text); + } + + SenderLineEdit.Clear(); + } + + public void ReceiveLine(string text) + { + var formatted = new FormattedMessage(1); + formatted.AddText(text); + TextOutput.AddMessage(formatted); + } + } +} diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml index 98167d55c8..1e331bc52d 100644 --- a/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml +++ b/Content.Client/Administration/UI/Tabs/AdminTab/AdminTab.xaml @@ -7,7 +7,7 @@ MinSize="50 50"> - + diff --git a/Content.Client/Administration/UI/Tabs/AdminTab/KickWindow.xaml b/Content.Client/Administration/UI/Tabs/AdminTab/KickWindow.xaml index c1ee96cfe6..1c68ad5799 100644 --- a/Content.Client/Administration/UI/Tabs/AdminTab/KickWindow.xaml +++ b/Content.Client/Administration/UI/Tabs/AdminTab/KickWindow.xaml @@ -1,14 +1,17 @@  + Title="{Loc admin-kick-window-title}" MinSize="425 272"> - -