using System.Linq; using Content.Server.Afk.Events; using Content.Server.GameTicking; using Content.Shared.CCVar; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Server.Afk; /// /// Actively checks for AFK players regularly and issues an event whenever they go afk. /// public sealed class AFKSystem : EntitySystem { [Dependency] private readonly IAfkManager _afkManager = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly GameTicker _ticker = default!; private float _checkDelay; private TimeSpan _checkTime; private readonly HashSet _afkPlayers = new(); public override void Initialize() { base.Initialize(); _playerManager.PlayerStatusChanged += OnPlayerChange; _configManager.OnValueChanged(CCVars.AfkTime, SetAfkDelay, true); } private void SetAfkDelay(float obj) { _checkDelay = obj; } private void OnPlayerChange(object? sender, SessionStatusEventArgs e) { switch (e.NewStatus) { case SessionStatus.Disconnected: _afkPlayers.Remove(e.Session); break; } } public override void Shutdown() { base.Shutdown(); _afkPlayers.Clear(); _playerManager.PlayerStatusChanged -= OnPlayerChange; _configManager.UnsubValueChanged(CCVars.AfkTime, SetAfkDelay); } public override void Update(float frameTime) { base.Update(frameTime); if (_ticker.RunLevel != GameRunLevel.InRound) { _afkPlayers.Clear(); _checkTime = TimeSpan.Zero; return; } // TODO: Should also listen to the input events for more accurate timings. if (_timing.CurTime < _checkTime) return; _checkTime = _timing.CurTime + TimeSpan.FromSeconds(_checkDelay); foreach (var session in Filter.GetAllPlayers()) { if (session.Status != SessionStatus.InGame) continue; var pSession = (IPlayerSession) session; var isAfk = _afkManager.IsAfk(pSession); if (isAfk && _afkPlayers.Add(pSession)) { var ev = new AFKEvent(pSession); RaiseLocalEvent(ref ev); continue; } if (!isAfk && _afkPlayers.Remove(pSession)) { var ev = new UnAFKEvent(pSession); RaiseLocalEvent(ref ev); continue; } } } }