using Content.Shared.CCVar;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Input;
using Robust.Shared.Timing;
namespace Content.Server.Afk
{
///
/// Tracks AFK (away from keyboard) status for players.
///
///
public interface IAfkManager
{
///
/// Check whether this player is currently AFK.
///
/// The player to check.
/// True if the player is AFK, false otherwise.
bool IsAfk(IPlayerSession player);
///
/// Resets AFK status for the player as if they just did an action and are definitely not AFK.
///
/// The player to set AFK status for.
void PlayerDidAction(IPlayerSession player);
void Initialize();
}
[UsedImplicitly]
public sealed class AfkManager : IAfkManager, IEntityEventSubscriber
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IConsoleHost _consoleHost = default!;
private readonly Dictionary _lastActionTimes = new();
public void Initialize()
{
// Connecting, console commands and input commands all reset AFK status.
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
_consoleHost.AnyCommandExecuted += ConsoleHostOnAnyCommandExecuted;
_entityManager.EventBus.SubscribeSessionEvent(
EventSource.Network,
this,
HandleInputCmd);
}
public void PlayerDidAction(IPlayerSession player)
{
if (player.Status == SessionStatus.Disconnected)
// Make sure we don't re-add to the dictionary if the player is disconnected now.
return;
_lastActionTimes[player] = _gameTiming.RealTime;
}
public bool IsAfk(IPlayerSession player)
{
if (!_lastActionTimes.TryGetValue(player, out var time))
// Some weird edge case like disconnected clients. Just say true I guess.
return true;
var timeOut = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.AfkTime));
return _gameTiming.RealTime - time > timeOut;
}
private void PlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Disconnected)
{
_lastActionTimes.Remove(e.Session);
return;
}
PlayerDidAction(e.Session);
}
private void ConsoleHostOnAnyCommandExecuted(IConsoleShell shell, string commandname, string argstr, string[] args)
{
if (shell.Player is IPlayerSession player)
PlayerDidAction(player);
}
private void HandleInputCmd(FullInputCmdMessage msg, EntitySessionEventArgs args)
{
PlayerDidAction((IPlayerSession) args.SenderSession);
}
}
}