CatStation/Content.Server/Afk/AfkSystem.cs
Princess Cheeseballs 2c6ffecf62
Move GameTicker to Shared (#45834)
* Scope creep my ass

* why was this shit not pushed????

* gone...

* fix that

* review plus buildd

* asfsafsafsafsafas

* grug

* burber

* I'll fix this later

* shrimply die

* also this

* it's snerf or snuthin

* assaads

* round snend

* aaaa

* fix this shit for real

* actually, don't start GameRules until MapInit now that I think aboot it.

* forgot u

* don't actually need this part nullable

* Deffer comp removal

* one liner

* do that too

* chungus

* Honestly makes more sense here, plus WHY are we doing all that shit on "GameRuleAdded" rather than GameRuleStarted(?)

* deferred removal :V

* aaaaaaaaaaaaaaa

* whhop

* Whoop

* better

* landmine that I placed for myself then forgot about lmao

* cleanup

* FUCKING `!`

* gotovo

* amogus

* please don't check by metadata I beg

* waawsasfdfsasfa

* sfafsasfafsas

* fdsaasffsasfa

* code

* fix

* fasasffasfasfsad

* chungaas

* I may be stupid

* gra

* graaaaaaaaaaaaaaaaa

* comment

* gra

* clearing is not predicted and would require major refactors, so don't cache on client.

* lame shit :P

* last holes

* nukeopsfsafsasafsfafsa

* why  was that shit public bruh

* bubrbgwahrghfaka

* dynamic

* revert change that did nothing lmao
2026-09-16 19:34:19 +00:00

205 lines
6.2 KiB
C#

using Content.Server.Administration.Managers;
using Content.Server.GameTicking;
using Content.Shared.Afk.Events;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Instruments;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Input;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Afk;
/// <summary>
/// Actively checks for AFK players regularly and issues an event whenever they go afk.
/// </summary>
public sealed partial class AfkSystem : EntitySystem
{
[Dependency] private IAdminManager _admin = default!;
[Dependency] private IAfkManager _afkManager = default!;
[Dependency] private IPlayerManager _playerManager = default!;
[Dependency] private IGameTiming _timing = default!;
[Dependency] private ServerGameTicker _ticker = default!;
[Dependency] private IConfigurationManager _cfg = default!;
/// <summary>
/// Don't need to do it every tick.
/// </summary>
private const float CheckDelay = 10f;
private TimeSpan _nextCheckTime;
private float _adminAfkTime;
private float _afkTime;
private readonly HashSet<ICommonSession> _afkPlayers = new();
public override void Initialize()
{
base.Initialize();
_playerManager.PlayerStatusChanged += OnPlayerChange;
_afkManager.PlayerDidActionEvent += OnPlayerAction;
Subs.CVar(_cfg, CCVars.AfkTime, OnAfkTimeChanged, true);
Subs.CVar(_cfg, CCVars.AdminAfkTime, OnAdminAfkTimeChanged, true);
SubscribeNetworkEvent<FullInputCmdMessage>(HandleInputCmd);
// Temporary until instruments use BUIs like normal.
SubscribeNetworkEvent<InstrumentStartMidiEvent>(HandleMidiStart);
SubscribeNetworkEvent<InstrumentStopMidiEvent>(HandleMidiStop);
SubscribeNetworkEvent<InstrumentMidiEventEvent>(HandleMidiEvent);
SubscribeNetworkEvent<InstrumentSetChannelsEvent>(HandleMidiSetChannels);
SubscribeLocalEvent<BoundUserInterfaceMessageReceivedEvent>(OnBoundUiMessageReceived);
}
private void HandleInputCmd(FullInputCmdMessage msg, EntitySessionEventArgs args)
{
if (!_playerManager.KeyMap.TryGetKeyFunction(msg.InputFunctionId, out _))
return;
if (!Enum.IsDefined(msg.State))
return;
_afkManager.PlayerDidAction(args.SenderSession);
}
private void HandleMidiStart(InstrumentStartMidiEvent msg, EntitySessionEventArgs args)
{
_afkManager.PlayerDidAction(args.SenderSession);
}
private void HandleMidiStop(InstrumentStopMidiEvent msg, EntitySessionEventArgs args)
{
_afkManager.PlayerDidAction(args.SenderSession);
}
private void HandleMidiEvent(InstrumentMidiEventEvent msg, EntitySessionEventArgs args)
{
_afkManager.PlayerDidAction(args.SenderSession);
}
private void HandleMidiSetChannels(InstrumentSetChannelsEvent msg, EntitySessionEventArgs args)
{
_afkManager.PlayerDidAction(args.SenderSession);
}
private void OnBoundUiMessageReceived(ref BoundUserInterfaceMessageReceivedEvent args)
{
if (!TryComp<ActorComponent>(args.Actor, out var actor))
return;
_afkManager.PlayerDidAction(actor.PlayerSession);
}
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;
_afkManager.PlayerDidActionEvent -= OnPlayerAction;
_cfg.UnsubValueChanged(CCVars.AfkTime, OnAfkTimeChanged);
}
private void OnPlayerAction(ICommonSession session)
{
if (!_afkPlayers.Remove(session))
return;
var ev = new UnAfkEvent(session);
RaiseLocalEvent(ref ev);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// If disabled then ignore flagging anything (including admins).
if (_afkTime <= 0)
return;
if (_timing.CurTime < _nextCheckTime)
return;
_nextCheckTime = _timing.CurTime + TimeSpan.FromSeconds(CheckDelay);
// Flag everyone as non-afk unless the game is running.
if (!CanFlagAfk(_ticker.RunLevel))
{
MarkAllInGamePlayersActive();
// Technically we can double-fire afk events here but shouldn't matter.
// We just want AFK timers reset by the time we get into round.
return;
}
foreach (var pSession in Filter.GetAllPlayers())
{
if (pSession.Status != SessionStatus.InGame)
continue;
var isAfk = _afkManager.IsAfk(pSession);
// If admin afk timer disabled then ignore them.
if (_adminAfkTime == 0f && _admin.IsAdmin(pSession))
{
isAfk = false;
}
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);
}
}
}
private static bool CanFlagAfk(GameRunLevel runLevel)
{
return runLevel == GameRunLevel.InRound;
}
private void OnAdminAfkTimeChanged(float value)
{
_adminAfkTime = value;
}
private void OnAfkTimeChanged(float value)
{
var wasEnabled = _afkTime > 0;
var isEnabled = value > 0;
// Reset AFK timers if we turn it on / off
if (wasEnabled != isEnabled)
MarkAllInGamePlayersActive();
_afkTime = value;
}
private void MarkAllInGamePlayersActive()
{
foreach (var pSession in Filter.GetAllPlayers())
{
if (pSession.Status != SessionStatus.InGame)
continue;
_afkManager.PlayerDidAction(pSession);
}
}
}