1
0

VotingSystem.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using Content.Server.Administration.Managers;
  2. using Content.Server.Database;
  3. using Content.Server.GameTicking;
  4. using Content.Server.Ghost;
  5. using Content.Server.Roles.Jobs;
  6. using Content.Shared.CCVar;
  7. using Content.Shared.Ghost;
  8. using Content.Shared.Mind.Components;
  9. using Content.Shared.Voting;
  10. using Robust.Server.Player;
  11. using Robust.Shared.Configuration;
  12. using Robust.Shared.Network;
  13. using Robust.Shared.Player;
  14. using Robust.Shared.Timing;
  15. using System.Threading.Tasks;
  16. using Content.Shared.Players.PlayTimeTracking;
  17. namespace Content.Server.Voting;
  18. public sealed class VotingSystem : EntitySystem
  19. {
  20. [Dependency] private readonly IPlayerManager _playerManager = default!;
  21. [Dependency] private readonly IAdminManager _adminManager = default!;
  22. [Dependency] private readonly IServerDbManager _dbManager = default!;
  23. [Dependency] private readonly IGameTiming _gameTiming = default!;
  24. [Dependency] private readonly IConfigurationManager _cfg = default!;
  25. [Dependency] private readonly JobSystem _jobs = default!;
  26. [Dependency] private readonly GameTicker _gameTicker = default!;
  27. [Dependency] private readonly ISharedPlaytimeManager _playtimeManager = default!;
  28. public override void Initialize()
  29. {
  30. base.Initialize();
  31. SubscribeNetworkEvent<VotePlayerListRequestEvent>(OnVotePlayerListRequestEvent);
  32. }
  33. private async void OnVotePlayerListRequestEvent(VotePlayerListRequestEvent msg, EntitySessionEventArgs args)
  34. {
  35. if (!await CheckVotekickInitEligibility(args.SenderSession))
  36. {
  37. var deniedResponse = new VotePlayerListResponseEvent(new (NetUserId, NetEntity, string)[0], true);
  38. RaiseNetworkEvent(deniedResponse, args.SenderSession.Channel);
  39. return;
  40. }
  41. List<(NetUserId, NetEntity, string)> players = new();
  42. foreach (var player in _playerManager.Sessions)
  43. {
  44. if (args.SenderSession == player) continue;
  45. if (_adminManager.IsAdmin(player, false)) continue;
  46. if (player.AttachedEntity is not { Valid: true } attached)
  47. {
  48. var playerName = player.Name;
  49. var netEntity = NetEntity.Invalid;
  50. players.Add((player.UserId, netEntity, playerName));
  51. }
  52. else
  53. {
  54. var playerName = GetPlayerVoteListName(attached);
  55. var netEntity = GetNetEntity(attached);
  56. players.Add((player.UserId, netEntity, playerName));
  57. }
  58. }
  59. var response = new VotePlayerListResponseEvent(players.ToArray(), false);
  60. RaiseNetworkEvent(response, args.SenderSession.Channel);
  61. }
  62. public string GetPlayerVoteListName(EntityUid attached)
  63. {
  64. TryComp<MindContainerComponent>(attached, out var mind);
  65. var jobName = _jobs.MindTryGetJobName(mind?.Mind);
  66. var playerInfo = $"{Comp<MetaDataComponent>(attached).EntityName} ({jobName})";
  67. return playerInfo;
  68. }
  69. /// <summary>
  70. /// Used to check whether the player initiating a votekick is allowed to do so serverside.
  71. /// </summary>
  72. /// <param name="initiator">The session initiating the votekick.</param>
  73. public async Task<bool> CheckVotekickInitEligibility(ICommonSession? initiator)
  74. {
  75. if (initiator == null)
  76. return false;
  77. // Being an admin overrides the votekick eligibility
  78. if (initiator.AttachedEntity != null && _adminManager.IsAdmin(initiator.AttachedEntity.Value, false))
  79. return true;
  80. // If cvar enabled, skip the ghost requirement in the preround lobby
  81. if (!_cfg.GetCVar(CCVars.VotekickIgnoreGhostReqInLobby) || (_cfg.GetCVar(CCVars.VotekickIgnoreGhostReqInLobby) && _gameTicker.RunLevel != GameRunLevel.PreRoundLobby))
  82. {
  83. if (_cfg.GetCVar(CCVars.VotekickInitiatorGhostRequirement))
  84. {
  85. // Must be ghost
  86. if (!TryComp(initiator.AttachedEntity, out GhostComponent? ghostComp))
  87. return false;
  88. // Must have been dead for x seconds
  89. if ((int)_gameTiming.RealTime.Subtract(ghostComp.TimeOfDeath).TotalSeconds < _cfg.GetCVar(CCVars.VotekickEligibleVoterDeathtime))
  90. return false;
  91. }
  92. }
  93. // Must be whitelisted
  94. if (!await _dbManager.GetWhitelistStatusAsync(initiator.UserId) && _cfg.GetCVar(CCVars.VotekickInitiatorWhitelistedRequirement))
  95. return false;
  96. // Must be eligible to vote
  97. var playtime = _playtimeManager.GetPlayTimes(initiator);
  98. return playtime.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out TimeSpan overallTime) && (overallTime >= TimeSpan.FromHours(_cfg.GetCVar(CCVars.VotekickEligibleVoterPlaytime))
  99. || !_cfg.GetCVar(CCVars.VotekickInitiatorTimeRequirement));
  100. }
  101. /// <summary>
  102. /// Used to check whether the player being targetted for a votekick is a valid target.
  103. /// </summary>
  104. /// <param name="target">The session being targetted for a votekick.</param>
  105. public bool CheckVotekickTargetEligibility(ICommonSession? target)
  106. {
  107. if (target == null)
  108. return false;
  109. // Admins can't be votekicked
  110. if (target.AttachedEntity != null && _adminManager.IsAdmin(target.AttachedEntity.Value))
  111. return false;
  112. return true;
  113. }
  114. }