1
0

JobRequirementsManager.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Client.Lobby;
  3. using Content.Shared.CCVar;
  4. using Content.Shared.Players;
  5. using Content.Shared.Players.JobWhitelist;
  6. using Content.Shared.Players.PlayTimeTracking;
  7. using Content.Shared.Preferences;
  8. using Content.Shared.Roles;
  9. using Robust.Client;
  10. using Robust.Client.Player;
  11. using Robust.Shared.Configuration;
  12. using Robust.Shared.Network;
  13. using Robust.Shared.Player;
  14. using Robust.Shared.Prototypes;
  15. using Robust.Shared.Utility;
  16. namespace Content.Client.Players.PlayTimeTracking;
  17. public sealed class JobRequirementsManager : ISharedPlaytimeManager
  18. {
  19. [Dependency] private readonly IBaseClient _client = default!;
  20. [Dependency] private readonly IClientNetManager _net = default!;
  21. [Dependency] private readonly IConfigurationManager _cfg = default!;
  22. [Dependency] private readonly IEntityManager _entManager = default!;
  23. [Dependency] private readonly IPlayerManager _playerManager = default!;
  24. [Dependency] private readonly IPrototypeManager _prototypes = default!;
  25. private readonly Dictionary<string, TimeSpan> _roles = new();
  26. private readonly List<string> _roleBans = new();
  27. private readonly List<string> _jobWhitelists = new();
  28. private ISawmill _sawmill = default!;
  29. public event Action? Updated;
  30. public void Initialize()
  31. {
  32. _sawmill = Logger.GetSawmill("job_requirements");
  33. // Yeah the client manager handles role bans and playtime but the server ones are separate DEAL.
  34. _net.RegisterNetMessage<MsgRoleBans>(RxRoleBans);
  35. _net.RegisterNetMessage<MsgPlayTime>(RxPlayTime);
  36. _net.RegisterNetMessage<MsgJobWhitelist>(RxJobWhitelist);
  37. _client.RunLevelChanged += ClientOnRunLevelChanged;
  38. }
  39. private void ClientOnRunLevelChanged(object? sender, RunLevelChangedEventArgs e)
  40. {
  41. if (e.NewLevel == ClientRunLevel.Initialize)
  42. {
  43. // Reset on disconnect, just in case.
  44. _roles.Clear();
  45. _jobWhitelists.Clear();
  46. _roleBans.Clear();
  47. }
  48. }
  49. private void RxRoleBans(MsgRoleBans message)
  50. {
  51. _sawmill.Debug($"Received roleban info containing {message.Bans.Count} entries.");
  52. _roleBans.Clear();
  53. _roleBans.AddRange(message.Bans);
  54. Updated?.Invoke();
  55. }
  56. private void RxPlayTime(MsgPlayTime message)
  57. {
  58. _roles.Clear();
  59. // NOTE: do not assign _roles = message.Trackers due to implicit data sharing in integration tests.
  60. foreach (var (tracker, time) in message.Trackers)
  61. {
  62. _roles[tracker] = time;
  63. }
  64. /*var sawmill = Logger.GetSawmill("play_time");
  65. foreach (var (tracker, time) in _roles)
  66. {
  67. sawmill.Info($"{tracker}: {time}");
  68. }*/
  69. Updated?.Invoke();
  70. }
  71. private void RxJobWhitelist(MsgJobWhitelist message)
  72. {
  73. _jobWhitelists.Clear();
  74. _jobWhitelists.AddRange(message.Whitelist);
  75. Updated?.Invoke();
  76. }
  77. public bool IsAllowed(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
  78. {
  79. reason = null;
  80. if (_roleBans.Contains($"Job:{job.ID}"))
  81. {
  82. reason = FormattedMessage.FromUnformatted(Loc.GetString("role-ban"));
  83. return false;
  84. }
  85. if (!CheckWhitelist(job, out reason))
  86. return false;
  87. var player = _playerManager.LocalSession;
  88. if (player == null)
  89. return true;
  90. return CheckRoleRequirements(job, profile, out reason);
  91. }
  92. public bool CheckRoleRequirements(JobPrototype job, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
  93. {
  94. var reqs = _entManager.System<SharedRoleSystem>().GetJobRequirement(job);
  95. return CheckRoleRequirements(reqs, profile, out reason);
  96. }
  97. public bool CheckRoleRequirements(HashSet<JobRequirement>? requirements, HumanoidCharacterProfile? profile, [NotNullWhen(false)] out FormattedMessage? reason)
  98. {
  99. reason = null;
  100. if (requirements == null || !_cfg.GetCVar(CCVars.GameRoleTimers))
  101. return true;
  102. var reasons = new List<string>();
  103. foreach (var requirement in requirements)
  104. {
  105. if (requirement.Check(_entManager, _prototypes, profile, _roles, out var jobReason))
  106. continue;
  107. reasons.Add(jobReason.ToMarkup());
  108. }
  109. reason = reasons.Count == 0 ? null : FormattedMessage.FromMarkupOrThrow(string.Join('\n', reasons));
  110. return reason == null;
  111. }
  112. public bool CheckWhitelist(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason)
  113. {
  114. reason = default;
  115. if (!_cfg.GetCVar(CCVars.GameRoleWhitelist))
  116. return true;
  117. if (job.Whitelisted && !_jobWhitelists.Contains(job.ID))
  118. {
  119. reason = FormattedMessage.FromUnformatted(Loc.GetString("role-not-whitelisted"));
  120. return false;
  121. }
  122. return true;
  123. }
  124. public TimeSpan FetchOverallPlaytime()
  125. {
  126. return _roles.TryGetValue("Overall", out var overallPlaytime) ? overallPlaytime : TimeSpan.Zero;
  127. }
  128. public IEnumerable<KeyValuePair<string, TimeSpan>> FetchPlaytimeByRoles()
  129. {
  130. var jobsToMap = _prototypes.EnumeratePrototypes<JobPrototype>();
  131. foreach (var job in jobsToMap)
  132. {
  133. if (_roles.TryGetValue(job.PlayTimeTracker, out var locJobName))
  134. {
  135. yield return new KeyValuePair<string, TimeSpan>(job.Name, locJobName);
  136. }
  137. }
  138. }
  139. public IReadOnlyDictionary<string, TimeSpan> GetPlayTimes(ICommonSession session)
  140. {
  141. if (session != _playerManager.LocalSession)
  142. {
  143. return new Dictionary<string, TimeSpan>();
  144. }
  145. return _roles;
  146. }
  147. }