diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index efc9929cc0..64d9b05a32 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -496,6 +496,7 @@ public sealed class ChatUIController : UIController if (_admin.HasFlag(AdminFlags.Admin)) { FilterableChannels |= ChatChannel.Admin; + FilterableChannels |= ChatChannel.AdminAlert; FilterableChannels |= ChatChannel.AdminChat; CanSendChannels |= ChatSelectChannel.Admin; } diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs index 92149bfb80..4a3b9aa568 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs @@ -20,6 +20,7 @@ public sealed partial class ChannelFilterPopup : Popup ChatChannel.OOC, ChatChannel.Dead, ChatChannel.Admin, + ChatChannel.AdminAlert, ChatChannel.AdminChat, ChatChannel.Server }; diff --git a/Content.Server/AME/AMENodeGroup.cs b/Content.Server/AME/AMENodeGroup.cs index f0527966d9..7a347ffb6f 100644 --- a/Content.Server/AME/AMENodeGroup.cs +++ b/Content.Server/AME/AMENodeGroup.cs @@ -1,5 +1,7 @@ using System.Linq; +using Content.Server.Administration.Systems; using Content.Server.AME.Components; +using Content.Server.Chat.Managers; using Content.Server.Explosion.EntitySystems; using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.Nodes; @@ -27,6 +29,8 @@ namespace Content.Server.AME [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly IChatManager _chat = default!; + public AMEControllerComponent? MasterController => _masterController; private readonly List _cores = new(); @@ -133,10 +137,21 @@ namespace Content.Server.AME if (instability != 0) { overloading = true; + var integrityCheck = 100; foreach(AMEShieldComponent core in _cores) { + var oldIntegrity = core.CoreIntegrity; core.CoreIntegrity -= instability; + + if (oldIntegrity > 95 + && core.CoreIntegrity <= 95 + && core.CoreIntegrity < integrityCheck) + integrityCheck = core.CoreIntegrity; } + + // Admin alert + if (integrityCheck != 100 && _masterController != null) + _chat.SendAdminAlert($"AME overloading: {_entMan.ToPrettyString(_masterController.Owner)}"); } } // Note the float conversions. The maths will completely fail if not done using floats. diff --git a/Content.Server/AME/Components/AMEControllerComponent.cs b/Content.Server/AME/Components/AMEControllerComponent.cs index 032486c82e..1a398a5eb5 100644 --- a/Content.Server/AME/Components/AMEControllerComponent.cs +++ b/Content.Server/AME/Components/AMEControllerComponent.cs @@ -1,5 +1,7 @@ using System.Linq; using Content.Server.Administration.Logs; +using Content.Server.Administration.Systems; +using Content.Server.Chat.Managers; using Content.Server.Mind.Components; using Content.Server.NodeContainer; using Content.Server.Power.Components; @@ -20,6 +22,7 @@ namespace Content.Server.AME.Components [Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly IChatManager _chat = default!; [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(AMEControllerUiKey.Key); private bool _injecting; @@ -183,6 +186,10 @@ namespace Content.Server.AME.Components if (msg.Button == UiButton.ToggleInjection) _adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{_entities.ToPrettyString(mindComponent.Owner):player} has set the AME to {humanReadableState}"); + + // Admin alert + if (GetCoreCount() * 2 == InjectionAmount - 2 && msg.Button == UiButton.IncreaseFuel) + _chat.SendAdminAlert(player, $"increased AME over safe limit to {InjectionAmount}", mindComponent); } GetAMENodeGroup()?.UpdateCoreVisuals(); diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 9de05e952d..48751e02c6 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -83,6 +83,15 @@ namespace Content.Server.Administration.Systems } } + public PlayerInfo? GetCachedPlayerInfo(NetUserId? netUserId) + { + if (netUserId == null) + return null; + + _playerList.TryGetValue(netUserId.Value, out var value); + return value ?? null; + } + private void OnIdentityChanged(IdentityChangedEvent ev) { if (!TryComp(ev.CharacterEntity, out var actor)) diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 3432321b31..90924188b2 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -1,6 +1,8 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; +using Content.Server.Administration.Systems; +using Content.Server.Mind.Components; using Content.Server.MoMMI; using Content.Server.Preferences.Managers; using Content.Server.Station.Systems; @@ -39,6 +41,7 @@ namespace Content.Server.Chat.Managers [Dependency] private readonly IServerPreferencesManager _preferencesManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly INetConfigurationManager _netConfigManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; /// /// The maximum length a player-sent message can be sent @@ -103,6 +106,31 @@ namespace Content.Server.Chat.Managers _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Admin announcement: {message}"); } + public void SendAdminAlert(string message) + { + var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient); + + var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", + ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); + + ChatMessageToMany(ChatChannel.AdminAlert, message, wrappedMessage, default, false, true, clients); + } + + public void SendAdminAlert(EntityUid player, string message, MindComponent? mindComponent = null) + { + if(mindComponent == null && !_entityManager.TryGetComponent(player, out mindComponent)) + { + SendAdminAlert(message); + return; + } + + var adminSystem = _entityManager.System(); + var antag = mindComponent.Mind!.UserId != null + && (adminSystem.GetCachedPlayerInfo(mindComponent.Mind!.UserId.Value)?.Antag ?? false); + + SendAdminAlert($"{mindComponent.Mind!.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}"); + } + public void SendHookOOC(string sender, string message) { if (!_oocEnabled && _configurationManager.GetCVar(CCVars.DisablingOOCDisablesRelay)) diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index b860da9624..6d74f76422 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -1,3 +1,4 @@ +using Content.Server.Mind.Components; using Content.Shared.Chat; using Robust.Server.Player; using Robust.Shared.Network; @@ -22,6 +23,8 @@ namespace Content.Server.Chat.Managers void SendHookOOC(string sender, string message); void SendAdminAnnouncement(string message); + void SendAdminAlert(string message); + void SendAdminAlert(EntityUid player, string message, MindComponent? mindComponent = null); void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0); diff --git a/Content.Shared/Chat/ChatChannel.cs b/Content.Shared/Chat/ChatChannel.cs index 3ea6435908..a80deda37a 100644 --- a/Content.Shared/Chat/ChatChannel.cs +++ b/Content.Shared/Chat/ChatChannel.cs @@ -64,15 +64,20 @@ namespace Content.Shared.Chat /// Admin = 1 << 10, + /// + /// Admin alerts, messages likely of elevated importance to admins + /// + AdminAlert = 1 << 11, + /// /// Admin chat /// - AdminChat = 1 << 11, + AdminChat = 1 << 12, /// /// Unspecified. /// - Unspecified = 1 << 12, + Unspecified = 1 << 13, /// /// Channels considered to be IC. diff --git a/Content.Shared/Chat/ChatChannelExtensions.cs b/Content.Shared/Chat/ChatChannelExtensions.cs index 505f032331..9b707a53a0 100644 --- a/Content.Shared/Chat/ChatChannelExtensions.cs +++ b/Content.Shared/Chat/ChatChannelExtensions.cs @@ -12,6 +12,7 @@ public static class ChatChannelExtensions ChatChannel.OOC => Color.LightSkyBlue, ChatChannel.Dead => Color.MediumPurple, ChatChannel.Admin => Color.Red, + ChatChannel.AdminAlert => Color.Red, ChatChannel.AdminChat => Color.HotPink, ChatChannel.Whisper => Color.DarkGray, _ => Color.LightGray diff --git a/Resources/Locale/en-US/chat/ui/chat-box.ftl b/Resources/Locale/en-US/chat/ui/chat-box.ftl index 29b3403aa3..ddf52d8be8 100644 --- a/Resources/Locale/en-US/chat/ui/chat-box.ftl +++ b/Resources/Locale/en-US/chat/ui/chat-box.ftl @@ -14,6 +14,7 @@ hud-chatbox-select-channel-Visual = Actions hud-chatbox-select-channel-Radio = Radio hud-chatbox-channel-Admin = Admin Misc +hud-chatbox-channel-AdminAlert = Admin Alert hud-chatbox-channel-AdminChat = Admin Chat hud-chatbox-channel-Dead = Dead hud-chatbox-channel-Emotes = Emotes