diff --git a/Content.Server/Access/Systems/AccessSystem.cs b/Content.Server/Access/Systems/AccessSystem.cs index af5c672008..f185d0ff98 100644 --- a/Content.Server/Access/Systems/AccessSystem.cs +++ b/Content.Server/Access/Systems/AccessSystem.cs @@ -1,126 +1,7 @@ using Content.Shared.Access.Systems; -using Content.Shared.Access.Components; -using Content.Server.Station.Systems; -using Content.Server.AlertLevel; -using Content.Shared.Station.Components; -using Timer = Robust.Shared.Timing.Timer; -using Content.Server.Chat.Systems; -using System.Threading; -namespace Content.Server.Access.Systems; //Sunrise-edited +namespace Content.Server.Access.Systems; public sealed class AccessSystem : SharedAccessSystem { - [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAlertLevelChanged); - } - private readonly CancellationTokenSource? _timerCancel; - - /// - /// Запускает таймер и выводит объявление о смене доступов через 1 минуту - /// - - private void OnAlertLevelChanged(AlertAccessesEvent ev) - { - if (!TryComp(ev.Station, out var alert)) - return; - - if (alert.AlertLevels == null) - return; - - var levels = new Dictionary - { - { "green", "access-system-accesses-delay-green" }, - { "blue", "access-system-accesses-delay-blue" }, - { "red", "access-system-accesses-delay-red" }, - { "yellow", "access-system-accesses-delay-yellow" }, - { "gamma", "access-system-accesses-delay-gamma" }, - }; - - foreach (var announce in levels) - { - if (alert.CurrentLevel.Contains(announce.Key)) - { - Timer.Spawn(TimeSpan.FromMinutes(1), () => AlertAccessesDelay(ev), _timerCancel?.Token ?? default); - _chatSystem.DispatchStationAnnouncement(ev.Station, - Loc.GetString(announce.Value), - playDefault: true, - colorOverride: Color.Yellow, - sender: Loc.GetString("access-system-sender")); - } - } - if (alert.CurrentLevel == "delta") - AlertAccessesDelay(ev); - - } - - /// - /// Устанавливает доступы спустя 1 минуту после начала таймера - /// - private void AlertAccessesDelay(AlertAccessesEvent ev) - { - _chatSystem.DispatchStationAnnouncement(ev.Station, - Loc.GetString("access-system-accesses-established"), - playDefault: true, - colorOverride: Color.Yellow, - sender: Loc.GetString("access-system-sender")); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var reader, out var xform)) - { - if (CompOrNull(xform.GridUid)?.Station != ev.Station) - continue; - - if (!TryComp(uid, out var comp)) - return; - - if (comp.AlertAccesses.Count == 0) - continue; - - Update((uid, reader)); - Dirty(uid, reader); - _timerCancel?.Cancel(); - } - } - - /// - /// Устанавливает значение из прототипа в зависимости от кода - /// - public void Update(Entity entity) - { - - if (!TryComp(_station.GetOwningStation(entity.Owner), out var alerts)) - return; - - if (alerts.AlertLevels == null) - return; - - var alertLevels = new Dictionary - { - { "blue", AccessReaderComponent.CurrentAlertLevel.blue }, - { "red", AccessReaderComponent.CurrentAlertLevel.red }, - { "yellow", AccessReaderComponent.CurrentAlertLevel.yellow }, - { "gamma", AccessReaderComponent.CurrentAlertLevel.gamma }, - { "delta", AccessReaderComponent.CurrentAlertLevel.delta } - }; - - entity.Comp.Group = string.Empty; // Значение по умолчанию - foreach (var level in alertLevels) - { - if (alerts.CurrentLevel.Contains(level.Key)) - { - if (entity.Comp.AlertAccesses.TryGetValue(level.Value, out var value)) - { - entity.Comp.Group = value; - break; - } - } - } - } } diff --git a/Content.Server/AlertLevel/AlertLevelPrototype.cs b/Content.Server/AlertLevel/AlertLevelPrototype.cs index 4f91864e4b..5fc97d693b 100644 --- a/Content.Server/AlertLevel/AlertLevelPrototype.cs +++ b/Content.Server/AlertLevel/AlertLevelPrototype.cs @@ -1,3 +1,4 @@ +using Content.Server._Sunrise.ExtendedAccess; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -72,8 +73,11 @@ public sealed partial class AlertLevelDetail /// [DataField("shuttleTime")] public TimeSpan ShuttleTime { get; private set; } = TimeSpan.FromMinutes(5); - // Sunrise-Start - [DataField("forceEndRound")] public bool ForceEndRound { get; private set; } = false; - // Sunrise-End + // Sunrise added start + [DataField] public bool ForceEndRound; + + [DataField] public ExtendedAccessOptions? ExtendedAccessOptions; + + // Sunrise added end } diff --git a/Content.Server/AlertLevel/AlertLevelSystem.cs b/Content.Server/AlertLevel/AlertLevelSystem.cs index dc3241f5a9..9355f176b4 100644 --- a/Content.Server/AlertLevel/AlertLevelSystem.cs +++ b/Content.Server/AlertLevel/AlertLevelSystem.cs @@ -165,6 +165,9 @@ public sealed class AlertLevelSystem : EntitySystem component.ActiveDelay = true; } + // Sunrise added - добавил сохраненый прежний уровень для системы автодоступов + var previousLevel = component.CurrentLevel; + component.CurrentLevel = level; component.IsLevelLocked = locked; @@ -212,7 +215,8 @@ public sealed class AlertLevelSystem : EntitySystem } // Sunrise-End - RaiseLocalEvent(new AlertLevelChangedEvent(station, level)); + // Sunrise edit - добавил прежний уровень для системы автодоступов + RaiseLocalEvent(new AlertLevelChangedEvent(station, level, previousLevel)); } } @@ -227,9 +231,12 @@ public sealed class AlertLevelChangedEvent : EntityEventArgs public EntityUid Station { get; } public string AlertLevel { get; } - public AlertLevelChangedEvent(EntityUid station, string alertLevel) + public string PreviousLevel; // Sunrise added - прежний уровень для системы автодоступов + + public AlertLevelChangedEvent(EntityUid station, string alertLevel, string previousLevel) { Station = station; AlertLevel = alertLevel; + PreviousLevel = previousLevel; // Sunrise added - прежний уровень для системы автодоступов } } diff --git a/Content.Server/AlertLevel/Commands/SetAlertLevelCommand.cs b/Content.Server/AlertLevel/Commands/SetAlertLevelCommand.cs index 7306564293..16800711ff 100644 --- a/Content.Server/AlertLevel/Commands/SetAlertLevelCommand.cs +++ b/Content.Server/AlertLevel/Commands/SetAlertLevelCommand.cs @@ -77,7 +77,6 @@ namespace Content.Server.AlertLevel.Commands } _entitySystems.GetEntitySystem().SetLevel(stationUid.Value, level, true, true, true, locked); - _entManager.EventBus.RaiseLocalEvent(stationUid.Value, new AlertAccessesEvent(stationUid.Value), true); // Sunrise-added } private string[] GetStationLevelNames(EntityUid station) diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index c00fb7121c..394c229091 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -223,7 +223,6 @@ namespace Content.Server.Communications if (stationUid != null) { _alertLevelSystem.SetLevel(stationUid.Value, message.Level, true, true); - RaiseLocalEvent(new AlertAccessesEvent(stationUid.Value)); // Sunrise-added } } diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 5c1014b26f..accbdf06c4 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -477,10 +477,9 @@ public sealed class NukeSystem : EntitySystem // The nuke may not be on a station, so it's more important to just // let people know that a nuclear bomb was armed in their vicinity instead. // Otherwise, you could set every station to whatever AlertLevelOnActivate is. - if (stationUid != null) // Sunrise-edited + if (stationUid != null) { _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnActivate, true, true, true, true); - RaiseLocalEvent(new AlertAccessesEvent(stationUid.Value)); } var pos = _transform.GetMapCoordinates(uid, xform: nukeXform); @@ -530,10 +529,9 @@ public sealed class NukeSystem : EntitySystem return; var stationUid = _station.GetOwningStation(uid); - if (stationUid != null) // Sunrise-edited + if (stationUid != null) { _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnDeactivate, true, true, true); - RaiseLocalEvent(new AlertAccessesEvent(stationUid.Value)); } // warn a crew diff --git a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs index 6f6af23fbb..916d7d1688 100644 --- a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs +++ b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs @@ -19,6 +19,5 @@ public sealed class AlertLevelInterceptionRule : StationEventSystem -/// Проставил этот ивент во всех удовлетворяющих случаях смены кода. -/// -public sealed class AlertAccessesEvent : EntityEventArgs -{ - public EntityUid Station { get; } - - public AlertAccessesEvent(EntityUid station) - { - Station = station; - } -} diff --git a/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessStuff.cs b/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessStuff.cs new file mode 100644 index 0000000000..cbfdddcf6f --- /dev/null +++ b/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessStuff.cs @@ -0,0 +1,8 @@ +namespace Content.Server._Sunrise.ExtendedAccess; + +[DataDefinition] +public partial record struct ExtendedAccessOptions +{ + [DataField] public string? Announcement; + [DataField] public TimeSpan Delay = TimeSpan.FromSeconds(60); +} diff --git a/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessSystem.cs b/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessSystem.cs new file mode 100644 index 0000000000..d5787af878 --- /dev/null +++ b/Content.Server/_Sunrise/ExtendedAccess/ExtendedAccessSystem.cs @@ -0,0 +1,114 @@ +using System.Threading; +using Content.Server.AlertLevel; +using Content.Server.Chat.Systems; +using Content.Shared.Access.Components; +using Content.Shared.GameTicking; +using Content.Shared.Station.Components; +using Timer = Robust.Shared.Timing.Timer; + +namespace Content.Server._Sunrise.ExtendedAccess; + +public sealed class ExtendedAccessSystem : EntitySystem +{ + [Dependency] private readonly ChatSystem _chat = default!; + + private static CancellationTokenSource _token = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAlertLevelChanged); + + SubscribeLocalEvent(_ => RecreateToken()); + } + + + /// + /// Запускает таймер и выводит объявление о смене доступов через некоторое время + /// + private void OnAlertLevelChanged(AlertLevelChangedEvent ev) + { + // Это случай первичного установления кода(зеленый) по умолчанию + // Чтобы в начале раунда не слышать, что доступы изменились на зеленый + if (ev.PreviousLevel == string.Empty) + return; + + if (!TryComp(ev.Station, out var alert)) + return; + + if (alert.AlertLevels == null) + return; + + if (!alert.AlertLevels.Levels.TryGetValue(alert.CurrentLevel, out var currentLevelDetail)) + return; + + var options = currentLevelDetail.ExtendedAccessOptions; + + if (options == null) + return; + + // Предотвращение стаканье смены доступов. Доступы должны сменяться только на последний код угрозы. + RecreateToken(); + + Timer.Spawn(options.Value.Delay, () => AfterDelay((ev.Station, alert)), _token.Token); + + if (options.Value.Announcement != null) + { + // В строке локализации оповещения обязательно должно быть указан параметр для времени + var message = Loc.GetString(options.Value.Announcement, ("time", options.Value.Delay.TotalSeconds)); + + _chat.DispatchStationAnnouncement(ev.Station, + Loc.GetString(message), + colorOverride: Color.Yellow, + sender: Loc.GetString("access-system-sender")); + } + } + + /// + /// Проходится по всем сущностям, считывающим доступ. + /// Заставляет пересмотреть свои доступы в соответствии с текущим кодом угрозы + /// + private void AfterDelay(Entity station) + { + _chat.DispatchStationAnnouncement(station, + Loc.GetString("access-system-accesses-established"), + colorOverride: Color.Yellow, + sender: Loc.GetString("access-system-sender")); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var reader, out var xform)) + { + if (CompOrNull(xform.GridUid)?.Station != station) + continue; + + if (reader.AlertAccesses.Count == 0) + continue; + + UpdateAccess((uid, reader), station); + } + } + + /// + /// Устанавливает новые доступы в соответствии с текущим кодом угрозы. + /// Сбрасывает аварийные доступы, если не нашлось доступов при текущем коде угрозы. + /// + private void UpdateAccess(Entity ent, Entity station) + { + if (station.Comp.AlertLevels == null) + return; + + if (ent.Comp.AlertAccesses.TryGetValue(station.Comp.CurrentLevel, out var value)) + ent.Comp.Group = value; + else + ent.Comp.Group = null; + + Dirty(ent); + } + + private static void RecreateToken() + { + _token.Cancel(); + _token = new(); + } +} diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index 9e8cd67c94..54bd0b8d91 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -16,6 +16,21 @@ namespace Content.Shared.Access.Components; [RegisterComponent, NetworkedComponent] public sealed partial class AccessReaderComponent : Component { + // Sunrise added start + #region ExtendedAccess + + /// + /// Именно от Group происходит проверка аварийных доступов + /// + [ViewVariables(VVAccess.ReadWrite)] + public ProtoId? Group; + + [DataField, ViewVariables] + public Dictionary> AlertAccesses = new(); + + #endregion + // Sunrise added end + /// /// Whether or not the accessreader is enabled. /// If not, it will always let people through. @@ -23,29 +38,6 @@ public sealed partial class AccessReaderComponent : Component [DataField] public bool Enabled = true; - // Sunrise-start - - /// - /// Именно от Group происходит проверка аварийных доступов - /// - [ViewVariables(VVAccess.ReadWrite)] - public ProtoId Group = string.Empty; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField] - public Dictionary> AlertAccesses = new(); - - [Flags] - public enum CurrentAlertLevel : byte - { - blue, - red, - yellow, - gamma, - delta - } - // Sunrise-end - /// /// The set of tags that will automatically deny an allowed check, if any of them are present. /// @@ -126,7 +118,7 @@ public sealed class AccessReaderComponentState : ComponentState public List>> AccessLists; - public ProtoId Group; // Sunrise-alertAccesses, нужно для связывания клиента с сервером + public ProtoId? Group; // Sunrise-alertAccesses, нужно для связывания клиента с сервером public List<(NetEntity, uint)> AccessKeys; @@ -136,10 +128,10 @@ public sealed class AccessReaderComponentState : ComponentState public AccessReaderComponentState(bool enabled, HashSet> denyTags, List>> accessLists, - ProtoId group, + ProtoId? group, //Sunrise added List<(NetEntity, uint)> accessKeys, Queue accessLog, - int accessLogLimit) //Sunrise-edit + int accessLogLimit) { Enabled = enabled; DenyTags = denyTags; @@ -147,7 +139,7 @@ public sealed class AccessReaderComponentState : ComponentState AccessKeys = accessKeys; AccessLog = accessLog; AccessLogLimit = accessLogLimit; - Group = group; // Sunrise-alertAccesses + Group = group; // Sunrise added for alertAccesses } } diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index f5e2b023e8..40ae69831e 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -68,7 +68,7 @@ public sealed class AccessReaderSystem : EntitySystem component.AccessLists = new(state.AccessLists); component.DenyTags = new(state.DenyTags); component.AccessLog = new(state.AccessLog); - component.Group = new(state.Group); // Sunrise-alertAccesses + component.Group = state.Group != null ? new (state.Group) : null; // Sunrise added - автодоступы по коду component.AccessLogLimit = state.AccessLogLimit; } @@ -167,8 +167,10 @@ public sealed class AccessReaderSystem : EntitySystem if (!reader.Enabled) return true; - if (AreAccessTagsAllowedAlert(access, reader)) + // Sunrise added start + if (IsAccessAllowedByExtendedAccess(access, reader)) return true; + // Sunrise added end if (reader.ContainerAccessProvider == null) return IsAllowedInternal(access, stationKeys, reader); @@ -186,10 +188,10 @@ public sealed class AccessReaderSystem : EntitySystem if (!TryComp(entity, out AccessReaderComponent? containedReader)) continue; - // Sunrise-start - if (AreAccessTagsAllowedAlert(access, containedReader)) + // Sunrise added start + if (IsAccessAllowedByExtendedAccess(access, containedReader)) return true; - // Sunrise-end + // Sunrise added end if (IsAllowed(access, stationKeys, entity, containedReader)) return true; @@ -238,26 +240,18 @@ public sealed class AccessReaderSystem : EntitySystem /// /// Сравнивает список аварийных доступов с доступами на карте. /// - public bool AreAccessTagsAllowedAlert(ICollection> access, AccessReaderComponent reader) + public bool IsAccessAllowedByExtendedAccess(ICollection> access, AccessReaderComponent reader) { - if (reader.Group == string.Empty) - return false; - - if (!_prototype.TryIndex(reader.Group, out var accessTags)) - return false; - - if (accessTags == null) + if (!_prototype.TryIndex(reader.Group, out var accessTags)) return false; if (accessTags.Tags.Count == 0) return false; - foreach (var ent in accessTags.Tags) - { - if (access.Contains(ent)) - return true; - } - return false; + if (!accessTags.Tags.Any(access.Contains)) + return false; + + return true; } // Sunrise-end diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/access/systems/access-system.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/access/systems/access-system.ftl index 50238926bc..333b6e1304 100644 --- a/Resources/Locale/ru-RU/_strings/_sunrise/access/systems/access-system.ftl +++ b/Resources/Locale/ru-RU/_strings/_sunrise/access/systems/access-system.ftl @@ -1,7 +1,7 @@ access-system-sender = Система доступов access-system-accesses-established = Внимание! Доступы были перезаписаны. -access-system-accesses-delay-green = Зафиксировано установление зеленого кода! Запущен протокол автоматической перезаписи доступов. Все аварийные доступы будут стёрты через 1 минуту. -access-system-accesses-delay-blue = Зафиксировано установление синего кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат дополнительные доступы в технические помещения через 1 минуту. -access-system-accesses-delay-red = Зафиксировано установление красного кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат расширенный доступ через 1 минуту. -access-system-accesses-delay-yellow = Зафиксировано установление желтого кода! Запущен протокол автоматической перезаписи доступов. Атмосферные техники и Старший Инженер получат дополнительные доступы через 1 минуту. -access-system-accesses-delay-gamma = Зафиксировано установление гамма кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат расширенный доступ через 1 минуту. +access-system-accesses-delay-green = Зафиксировано установление зеленого кода! Запущен протокол автоматической перезаписи доступов. Все аварийные доступы будут стёрты через {$time} секунд. +access-system-accesses-delay-blue = Зафиксировано установление синего кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат дополнительные доступы в технические помещения через {$time} секунд. +access-system-accesses-delay-red = Зафиксировано установление красного кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат расширенный доступ через {$time} секунд. +access-system-accesses-delay-yellow = Зафиксировано установление желтого кода! Запущен протокол автоматической перезаписи доступов. Атмосферные техники и Старший Инженер получат дополнительные доступы через {$time} секунд. +access-system-accesses-delay-gamma = Зафиксировано установление гамма кода! Запущен протокол автоматической перезаписи доступов. Сотрудники службы безопасности получат расширенный доступ через {$time} секунд. diff --git a/Resources/Prototypes/AlertLevels/alert_levels.yml b/Resources/Prototypes/AlertLevels/alert_levels.yml index 15f73078b2..b81d62ec5f 100644 --- a/Resources/Prototypes/AlertLevels/alert_levels.yml +++ b/Resources/Prototypes/AlertLevels/alert_levels.yml @@ -7,6 +7,10 @@ color: Green emergencyLightColor: LawnGreen shuttleTime: 600 + # Sunrise edit start + extendedAccessOptions: + announcement: access-system-accesses-delay-green + # Sunrise edit end blue: announcement: alert-level-blue-announcement sound: /Audio/Misc/bluealert.ogg @@ -14,6 +18,10 @@ forceEnableEmergencyLights: true emergencyLightColor: DodgerBlue shuttleTime: 600 + # Sunrise edit start + extendedAccessOptions: + announcement: access-system-accesses-delay-blue + # Sunrise edit end violet: announcement: alert-level-violet-announcement sound: /Audio/Misc/notice1.ogg @@ -28,6 +36,10 @@ emergencyLightColor: Goldenrod forceEnableEmergencyLights: true shuttleTime: 600 + # Sunrise edit start + extendedAccessOptions: + announcement: access-system-accesses-delay-yellow + # Sunrise edit end red: announcement: alert-level-red-announcement sound: /Audio/Misc/redalert.ogg @@ -35,6 +47,10 @@ emergencyLightColor: Red forceEnableEmergencyLights: true shuttleTime: 600 #No reduction in time as we don't have swiping for red alert like in /tg/. Shuttle times are intended to create friction, so having a way to brainlessly bypass that would be dumb. + # Sunrise edit start + extendedAccessOptions: + announcement: access-system-accesses-delay-red + # Sunrise edit end gamma: announcement: alert-level-gamma-announcement selectable: false @@ -46,6 +62,10 @@ color: PaleVioletRed emergencyLightColor: PaleVioletRed forceEnableEmergencyLights: true + # Sunrise edit start + extendedAccessOptions: + announcement: access-system-accesses-delay-gamma + # Sunrise edit end delta: announcement: alert-level-delta-announcement selectable: false @@ -58,6 +78,10 @@ emergencyLightColor: Orange forceEnableEmergencyLights: true shuttleTime: 1200 + # Sunrise edit start + extendedAccessOptions: + delay: 0 + # Sunrise edit end epsilon: announcement: alert-level-epsilon-announcement selectable: false