| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- using Content.Server.Administration.Logs;
- using Content.Server.AlertLevel;
- using Content.Server.Chat.Systems;
- using Content.Server.DeviceNetwork.Components;
- using Content.Server.DeviceNetwork.Systems;
- using Content.Server.Interaction;
- using Content.Server.Popups;
- using Content.Server.RoundEnd;
- using Content.Server.Screens.Components;
- using Content.Server.Shuttles.Systems;
- using Content.Server.Station.Systems;
- using Content.Shared.Access.Components;
- using Content.Shared.Access.Systems;
- using Content.Shared.CCVar;
- using Content.Shared.Chat;
- using Content.Shared.Communications;
- using Content.Shared.Database;
- using Content.Shared.DeviceNetwork;
- using Content.Shared.IdentityManagement;
- using Content.Shared.Popups;
- using Robust.Server.GameObjects;
- using Robust.Shared.Configuration;
- namespace Content.Server.Communications
- {
- public sealed class CommunicationsConsoleSystem : EntitySystem
- {
- [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
- [Dependency] private readonly AlertLevelSystem _alertLevelSystem = default!;
- [Dependency] private readonly ChatSystem _chatSystem = default!;
- [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
- [Dependency] private readonly EmergencyShuttleSystem _emergency = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
- [Dependency] private readonly StationSystem _stationSystem = default!;
- [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
- [Dependency] private readonly IConfigurationManager _cfg = default!;
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- private const float UIUpdateInterval = 5.0f;
- public override void Initialize()
- {
- // All events that refresh the BUI
- SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
- SubscribeLocalEvent<CommunicationsConsoleComponent, ComponentInit>((uid, comp, _) => UpdateCommsConsoleInterface(uid, comp));
- SubscribeLocalEvent<RoundEndSystemChangedEvent>(_ => OnGenericBroadcastEvent());
- SubscribeLocalEvent<AlertLevelDelayFinishedEvent>(_ => OnGenericBroadcastEvent());
- // Messages from the BUI
- SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleSelectAlertLevelMessage>(OnSelectAlertLevelMessage);
- SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleAnnounceMessage>(OnAnnounceMessage);
- SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleBroadcastMessage>(OnBroadcastMessage);
- SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleCallEmergencyShuttleMessage>(OnCallShuttleMessage);
- SubscribeLocalEvent<CommunicationsConsoleComponent, CommunicationsConsoleRecallEmergencyShuttleMessage>(OnRecallShuttleMessage);
- // On console init, set cooldown
- SubscribeLocalEvent<CommunicationsConsoleComponent, MapInitEvent>(OnCommunicationsConsoleMapInit);
- }
- public override void Update(float frameTime)
- {
- var query = EntityQueryEnumerator<CommunicationsConsoleComponent>();
- while (query.MoveNext(out var uid, out var comp))
- {
- // TODO refresh the UI in a less horrible way
- if (comp.AnnouncementCooldownRemaining >= 0f)
- {
- comp.AnnouncementCooldownRemaining -= frameTime;
- }
- comp.UIUpdateAccumulator += frameTime;
- if (comp.UIUpdateAccumulator < UIUpdateInterval)
- continue;
- comp.UIUpdateAccumulator -= UIUpdateInterval;
- if (_uiSystem.IsUiOpen(uid, CommunicationsConsoleUiKey.Key))
- UpdateCommsConsoleInterface(uid, comp);
- }
- base.Update(frameTime);
- }
- public void OnCommunicationsConsoleMapInit(EntityUid uid, CommunicationsConsoleComponent comp, MapInitEvent args)
- {
- comp.AnnouncementCooldownRemaining = comp.InitialDelay;
- }
- /// <summary>
- /// Update the UI of every comms console.
- /// </summary>
- private void OnGenericBroadcastEvent()
- {
- var query = EntityQueryEnumerator<CommunicationsConsoleComponent>();
- while (query.MoveNext(out var uid, out var comp))
- {
- UpdateCommsConsoleInterface(uid, comp);
- }
- }
- /// <summary>
- /// Updates all comms consoles belonging to the station that the alert level was set on
- /// </summary>
- /// <param name="args">Alert level changed event arguments</param>
- private void OnAlertLevelChanged(AlertLevelChangedEvent args)
- {
- var query = EntityQueryEnumerator<CommunicationsConsoleComponent>();
- while (query.MoveNext(out var uid, out var comp))
- {
- var entStation = _stationSystem.GetOwningStation(uid);
- if (args.Station == entStation)
- UpdateCommsConsoleInterface(uid, comp);
- }
- }
- /// <summary>
- /// Updates the UI for all comms consoles.
- /// </summary>
- public void UpdateCommsConsoleInterface()
- {
- var query = EntityQueryEnumerator<CommunicationsConsoleComponent>();
- while (query.MoveNext(out var uid, out var comp))
- {
- UpdateCommsConsoleInterface(uid, comp);
- }
- }
- /// <summary>
- /// Updates the UI for a particular comms console.
- /// </summary>
- public void UpdateCommsConsoleInterface(EntityUid uid, CommunicationsConsoleComponent comp)
- {
- var stationUid = _stationSystem.GetOwningStation(uid);
- List<string>? levels = null;
- string currentLevel = default!;
- float currentDelay = 0;
- if (stationUid != null)
- {
- if (TryComp(stationUid.Value, out AlertLevelComponent? alertComp) &&
- alertComp.AlertLevels != null)
- {
- if (alertComp.IsSelectable)
- {
- levels = new();
- foreach (var (id, detail) in alertComp.AlertLevels.Levels)
- {
- if (detail.Selectable)
- {
- levels.Add(id);
- }
- }
- }
- currentLevel = alertComp.CurrentLevel;
- currentDelay = _alertLevelSystem.GetAlertLevelDelay(stationUid.Value, alertComp);
- }
- }
- _uiSystem.SetUiState(uid, CommunicationsConsoleUiKey.Key, new CommunicationsConsoleInterfaceState(
- CanAnnounce(comp),
- CanCallOrRecall(comp),
- levels,
- currentLevel,
- currentDelay,
- _roundEndSystem.ExpectedCountdownEnd
- ));
- }
- private static bool CanAnnounce(CommunicationsConsoleComponent comp)
- {
- return comp.AnnouncementCooldownRemaining <= 0f;
- }
- private bool CanUse(EntityUid user, EntityUid console)
- {
- if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent))
- {
- return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent);
- }
- return true;
- }
- private bool CanCallOrRecall(CommunicationsConsoleComponent comp)
- {
- // Defer to what the round end system thinks we should be able to do.
- if (_emergency.EmergencyShuttleArrived || !_roundEndSystem.CanCallOrRecall())
- return false;
- // Ensure that we can communicate with the shuttle (either call or recall)
- if (!comp.CanShuttle)
- return false;
- // Calling shuttle checks
- if (_roundEndSystem.ExpectedCountdownEnd is null)
- return true;
- // Recalling shuttle checks
- var recallThreshold = _cfg.GetCVar(CCVars.EmergencyRecallTurningPoint);
- // shouldn't really be happening if we got here
- if (_roundEndSystem.ShuttleTimeLeft is not { } left
- || _roundEndSystem.ExpectedShuttleLength is not { } expected)
- return false;
- return !(left.TotalSeconds / expected.TotalSeconds < recallThreshold);
- }
- private void OnSelectAlertLevelMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleSelectAlertLevelMessage message)
- {
- if (message.Actor is not { Valid: true } mob)
- return;
- if (!CanUse(mob, uid))
- {
- _popupSystem.PopupCursor(Loc.GetString("comms-console-permission-denied"), message.Actor, PopupType.Medium);
- return;
- }
- var stationUid = _stationSystem.GetOwningStation(uid);
- if (stationUid != null)
- {
- _alertLevelSystem.SetLevel(stationUid.Value, message.Level, true, true);
- }
- }
- private void OnAnnounceMessage(EntityUid uid, CommunicationsConsoleComponent comp,
- CommunicationsConsoleAnnounceMessage message)
- {
- var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
- var msg = SharedChatSystem.SanitizeAnnouncement(message.Message, maxLength);
- var author = Loc.GetString("comms-console-announcement-unknown-sender");
- if (message.Actor is { Valid: true } mob)
- {
- if (!CanAnnounce(comp))
- {
- return;
- }
- if (!CanUse(mob, uid))
- {
- _popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, message.Actor);
- return;
- }
- var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, mob);
- RaiseLocalEvent(tryGetIdentityShortInfoEvent);
- author = tryGetIdentityShortInfoEvent.Title;
- }
- comp.AnnouncementCooldownRemaining = comp.Delay;
- UpdateCommsConsoleInterface(uid, comp);
- var ev = new CommunicationConsoleAnnouncementEvent(uid, comp, msg, message.Actor);
- RaiseLocalEvent(ref ev);
- // allow admemes with vv
- Loc.TryGetString(comp.Title, out var title);
- title ??= comp.Title;
- msg += "\n" + Loc.GetString("comms-console-announcement-sent-by") + " " + author;
- if (comp.Global)
- {
- _chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: comp.Sound, colorOverride: comp.Color);
- _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Actor):player} has sent the following global announcement: {msg}");
- return;
- }
- _chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.Color);
- _adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Actor):player} has sent the following world announcement: {msg}");
- }
- private void OnBroadcastMessage(EntityUid uid, CommunicationsConsoleComponent component, CommunicationsConsoleBroadcastMessage message)
- {
- if (!TryComp<DeviceNetworkComponent>(uid, out var net))
- return;
- var payload = new NetworkPayload
- {
- [ScreenMasks.Text] = message.Message
- };
- _deviceNetworkSystem.QueuePacket(uid, null, payload, net.TransmitFrequency);
- _adminLogger.Add(LogType.DeviceNetwork, LogImpact.Low, $"{ToPrettyString(message.Actor):player} has sent the following broadcast: {message.Message:msg}");
- }
- private void OnCallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleCallEmergencyShuttleMessage message)
- {
- if (!CanCallOrRecall(comp))
- return;
- var mob = message.Actor;
- if (!CanUse(mob, uid))
- {
- _popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, message.Actor);
- return;
- }
- var ev = new CommunicationConsoleCallShuttleAttemptEvent(uid, comp, mob);
- RaiseLocalEvent(ref ev);
- if (ev.Cancelled)
- {
- _popupSystem.PopupEntity(ev.Reason ?? Loc.GetString("comms-console-shuttle-unavailable"), uid, message.Actor);
- return;
- }
- _roundEndSystem.RequestRoundEnd(uid);
- _adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(mob):player} has called the shuttle.");
- }
- private void OnRecallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleRecallEmergencyShuttleMessage message)
- {
- if (!CanCallOrRecall(comp))
- return;
- if (!CanUse(message.Actor, uid))
- {
- _popupSystem.PopupEntity(Loc.GetString("comms-console-permission-denied"), uid, message.Actor);
- return;
- }
- _roundEndSystem.CancelRoundEndCountdown(uid);
- _adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(message.Actor):player} has recalled the shuttle.");
- }
- }
- /// <summary>
- /// Raised on announcement
- /// </summary>
- [ByRefEvent]
- public record struct CommunicationConsoleAnnouncementEvent(EntityUid Uid, CommunicationsConsoleComponent Component, string Text, EntityUid? Sender)
- {
- public EntityUid Uid = Uid;
- public CommunicationsConsoleComponent Component = Component;
- public EntityUid? Sender = Sender;
- public string Text = Text;
- }
- /// <summary>
- /// Raised on shuttle call attempt. Can be cancelled
- /// </summary>
- [ByRefEvent]
- public record struct CommunicationConsoleCallShuttleAttemptEvent(EntityUid Uid, CommunicationsConsoleComponent Component, EntityUid? Sender)
- {
- public bool Cancelled = false;
- public EntityUid Uid = Uid;
- public CommunicationsConsoleComponent Component = Component;
- public EntityUid? Sender = Sender;
- public string? Reason;
- }
- }
|