Restart votes check player count and ghost levels (#23453)
* Restarts have max player count * Now counts ingame ghosts to determine if restart can be called * consistant cvar name * Added a player count with state conditional
This commit is contained in:
@@ -5,8 +5,10 @@ using Content.Server.Maps;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Voting;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -49,73 +51,117 @@ namespace Content.Server.Voting.Managers
|
||||
|
||||
private void CreateRestartVote(ICommonSession? initiator)
|
||||
{
|
||||
var alone = _playerManager.PlayerCount == 1 && initiator != null;
|
||||
var options = new VoteOptions
|
||||
{
|
||||
Title = Loc.GetString("ui-vote-restart-title"),
|
||||
Options =
|
||||
{
|
||||
(Loc.GetString("ui-vote-restart-yes"), "yes"),
|
||||
(Loc.GetString("ui-vote-restart-no"), "no"),
|
||||
(Loc.GetString("ui-vote-restart-abstain"), "abstain")
|
||||
},
|
||||
Duration = alone
|
||||
? TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VoteTimerAlone))
|
||||
: TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VoteTimerRestart)),
|
||||
InitiatorTimeout = TimeSpan.FromMinutes(5)
|
||||
};
|
||||
|
||||
if (alone)
|
||||
options.InitiatorTimeout = TimeSpan.FromSeconds(10);
|
||||
var playerVoteMaximum = _cfg.GetCVar(CCVars.VoteRestartMaxPlayers);
|
||||
var totalPlayers = _playerManager.Sessions.Count(session => session.Status != SessionStatus.Disconnected);
|
||||
|
||||
WirePresetVoteInitiator(options, initiator);
|
||||
|
||||
var vote = CreateVote(options);
|
||||
|
||||
vote.OnFinished += (_, _) =>
|
||||
{
|
||||
var votesYes = vote.VotesPerOption["yes"];
|
||||
var votesNo = vote.VotesPerOption["no"];
|
||||
var total = votesYes + votesNo;
|
||||
|
||||
var ratioRequired = _cfg.GetCVar(CCVars.VoteRestartRequiredRatio);
|
||||
if (total > 0 && votesYes / (float) total >= ratioRequired)
|
||||
{
|
||||
// Check if an admin is online, and ignore the passed vote if the cvar is enabled
|
||||
if (_cfg.GetCVar(CCVars.VoteRestartNotAllowedWhenAdminOnline) && _adminMgr.ActiveAdmins.Count() != 0)
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote attempted to pass, but an admin was online. {votesYes}/{votesNo}");
|
||||
}
|
||||
else // If the cvar is disabled or there's no admins on, proceed as normal
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote succeeded: {votesYes}/{votesNo}");
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-restart-succeeded"));
|
||||
var roundEnd = _entityManager.EntitySysManager.GetEntitySystem<RoundEndSystem>();
|
||||
roundEnd.EndRound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote failed: {votesYes}/{votesNo}");
|
||||
_chatManager.DispatchServerAnnouncement(
|
||||
Loc.GetString("ui-vote-restart-failed", ("ratio", ratioRequired)));
|
||||
}
|
||||
};
|
||||
|
||||
if (initiator != null)
|
||||
{
|
||||
// Cast yes vote if created the vote yourself.
|
||||
vote.CastVote(initiator, 0);
|
||||
}
|
||||
var ghostVotePercentageRequirement = _cfg.GetCVar(CCVars.VoteRestartGhostPercentage);
|
||||
var ghostCount = 0;
|
||||
|
||||
foreach (var player in _playerManager.Sessions)
|
||||
{
|
||||
if (player != initiator)
|
||||
_playerManager.UpdateState(player);
|
||||
if (player.Status != SessionStatus.Disconnected && _entityManager.HasComponent<GhostComponent>(player.AttachedEntity))
|
||||
{
|
||||
// Everybody else defaults to an abstain vote to say they don't mind.
|
||||
vote.CastVote(player, 2);
|
||||
ghostCount++;
|
||||
}
|
||||
}
|
||||
|
||||
var ghostPercentage = 0.0;
|
||||
if (totalPlayers > 0)
|
||||
{
|
||||
ghostPercentage = ((double)ghostCount / totalPlayers) * 100;
|
||||
}
|
||||
|
||||
var roundedGhostPercentage = (int)Math.Round(ghostPercentage);
|
||||
|
||||
if (totalPlayers <= playerVoteMaximum || roundedGhostPercentage >= ghostVotePercentageRequirement)
|
||||
{
|
||||
StartVote(initiator);
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyNotEnoughGhostPlayers(ghostVotePercentageRequirement, roundedGhostPercentage);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartVote(ICommonSession? initiator)
|
||||
{
|
||||
var alone = _playerManager.PlayerCount == 1 && initiator != null;
|
||||
var options = new VoteOptions
|
||||
{
|
||||
Title = Loc.GetString("ui-vote-restart-title"),
|
||||
Options =
|
||||
{
|
||||
(Loc.GetString("ui-vote-restart-yes"), "yes"),
|
||||
(Loc.GetString("ui-vote-restart-no"), "no"),
|
||||
(Loc.GetString("ui-vote-restart-abstain"), "abstain")
|
||||
},
|
||||
Duration = alone
|
||||
? TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VoteTimerAlone))
|
||||
: TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.VoteTimerRestart)),
|
||||
InitiatorTimeout = TimeSpan.FromMinutes(5)
|
||||
};
|
||||
|
||||
if (alone)
|
||||
options.InitiatorTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
WirePresetVoteInitiator(options, initiator);
|
||||
|
||||
var vote = CreateVote(options);
|
||||
|
||||
vote.OnFinished += (_, _) =>
|
||||
{
|
||||
var votesYes = vote.VotesPerOption["yes"];
|
||||
var votesNo = vote.VotesPerOption["no"];
|
||||
var total = votesYes + votesNo;
|
||||
|
||||
var ratioRequired = _cfg.GetCVar(CCVars.VoteRestartRequiredRatio);
|
||||
if (total > 0 && votesYes / (float) total >= ratioRequired)
|
||||
{
|
||||
// Check if an admin is online, and ignore the passed vote if the cvar is enabled
|
||||
if (_cfg.GetCVar(CCVars.VoteRestartNotAllowedWhenAdminOnline) && _adminMgr.ActiveAdmins.Count() != 0)
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote attempted to pass, but an admin was online. {votesYes}/{votesNo}");
|
||||
}
|
||||
else // If the cvar is disabled or there's no admins on, proceed as normal
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote succeeded: {votesYes}/{votesNo}");
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-restart-succeeded"));
|
||||
var roundEnd = _entityManager.EntitySysManager.GetEntitySystem<RoundEndSystem>();
|
||||
roundEnd.EndRound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote failed: {votesYes}/{votesNo}");
|
||||
_chatManager.DispatchServerAnnouncement(
|
||||
Loc.GetString("ui-vote-restart-failed", ("ratio", ratioRequired)));
|
||||
}
|
||||
};
|
||||
|
||||
if (initiator != null)
|
||||
{
|
||||
// Cast yes vote if created the vote yourself.
|
||||
vote.CastVote(initiator, 0);
|
||||
}
|
||||
|
||||
foreach (var player in _playerManager.Sessions)
|
||||
{
|
||||
if (player != initiator)
|
||||
{
|
||||
// Everybody else defaults to an abstain vote to say they don't mind.
|
||||
vote.CastVote(player, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifyNotEnoughGhostPlayers(int ghostPercentageRequirement, int roundedGhostPercentage)
|
||||
{
|
||||
// Logic to notify that there are not enough ghost players to start a vote
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Restart vote failed: Current Ghost player percentage:{roundedGhostPercentage.ToString()}% does not meet {ghostPercentageRequirement.ToString()}%");
|
||||
_chatManager.DispatchServerAnnouncement(
|
||||
Loc.GetString("ui-vote-restart-fail-not-enough-ghost-players", ("ghostPlayerRequirement", ghostPercentageRequirement)));
|
||||
}
|
||||
|
||||
private void CreatePresetVote(ICommonSession? initiator)
|
||||
|
||||
@@ -1256,6 +1256,18 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<bool> VoteRestartEnabled =
|
||||
CVarDef.Create("vote.restart_enabled", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Config for when the restart vote should be allowed to be called regardless with less than this amount of players.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> VoteRestartMaxPlayers =
|
||||
CVarDef.Create("vote.restart_max_players", 20, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Config for when the restart vote should be allowed to be called based on percentage of ghosts.
|
||||
///
|
||||
public static readonly CVarDef<int> VoteRestartGhostPercentage =
|
||||
CVarDef.Create("vote.restart_ghost_percentage", 75, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// See vote.enabled, but specific to preset votes
|
||||
/// </summary>
|
||||
|
||||
@@ -6,6 +6,7 @@ ui-vote-initiator-server = The server
|
||||
ui-vote-restart-title = Restart round
|
||||
ui-vote-restart-succeeded = Restart vote succeeded.
|
||||
ui-vote-restart-failed = Restart vote failed (need { TOSTRING($ratio, "P0") }).
|
||||
ui-vote-restart-fail-not-enough-ghost-players = Restart vote failed: A minimum of { $ghostPlayerRequirement }% ghost players is required to initiate a restart vote. Currently, there are not enough ghost players.
|
||||
ui-vote-restart-yes = Yes
|
||||
ui-vote-restart-no = No
|
||||
ui-vote-restart-abstain = Abstain
|
||||
|
||||
Reference in New Issue
Block a user