aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/vote_controller.cpp
diff options
context:
space:
mode:
authorJohn Schoenick <[email protected]>2015-09-09 18:35:41 -0700
committerJohn Schoenick <[email protected]>2015-09-09 18:35:41 -0700
commit0d8dceea4310fde5706b3ce1c70609d72a38efdf (patch)
treec831ef32c2c801a5c5a80401736b52c7b5a528ec /mp/src/game/server/vote_controller.cpp
parentUpdated the SDK with the latest code from the TF and HL2 branches. (diff)
downloadsource-sdk-2013-master.tar.xz
source-sdk-2013-master.zip
Updated the SDK with the latest code from the TF and HL2 branches.HEADmaster
Diffstat (limited to 'mp/src/game/server/vote_controller.cpp')
-rw-r--r--mp/src/game/server/vote_controller.cpp375
1 files changed, 227 insertions, 148 deletions
diff --git a/mp/src/game/server/vote_controller.cpp b/mp/src/game/server/vote_controller.cpp
index 7dab54bb..a29b449f 100644
--- a/mp/src/game/server/vote_controller.cpp
+++ b/mp/src/game/server/vote_controller.cpp
@@ -14,6 +14,7 @@
#ifdef TF_DLL
#include "tf/tf_gamerules.h"
+#include "tf/tf_voteissues.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@@ -38,15 +39,15 @@ LINK_ENTITY_TO_CLASS( vote_controller, CVoteController );
CVoteController *g_voteController = NULL;
-ConVar sv_vote_timer_duration("sv_vote_timer_duration", "15", FCVAR_DEVELOPMENTONLY, "How long to allow voting on an issue");
-ConVar sv_vote_command_delay("sv_vote_command_delay", "2", FCVAR_DEVELOPMENTONLY, "How long after a vote passes until the action happens", false, 0, true, 4.5);
-ConVar sv_allow_votes("sv_allow_votes", "1", FCVAR_NONE, "Allow voting?");
+ConVar sv_vote_timer_duration( "sv_vote_timer_duration", "15", FCVAR_DEVELOPMENTONLY, "How long to allow voting on an issue" );
+ConVar sv_vote_command_delay( "sv_vote_command_delay", "2", FCVAR_DEVELOPMENTONLY, "How long after a vote passes until the action happens", false, 0.f, true, 4.5f );
+ConVar sv_allow_votes( "sv_allow_votes", "1", FCVAR_NONE, "Allow voting?" );
ConVar sv_vote_failure_timer( "sv_vote_failure_timer", "300", FCVAR_NONE, "A vote that fails cannot be re-submitted for this long" );
#ifdef TF_DLL
ConVar sv_vote_failure_timer_mvm( "sv_vote_failure_timer_mvm", "120", FCVAR_NONE, "A vote that fails in MvM cannot be re-submitted for this long" );
#endif // TF_DLL
-ConVar sv_vote_creation_timer("sv_vote_creation_timer", "120", FCVAR_DEVELOPMENTONLY, "How often someone can individually call a vote.");
-ConVar sv_vote_quorum_ratio( "sv_vote_quorum_ratio", "0.6", FCVAR_HIDDEN, "The minimum ratio of players needed to vote on an issue to resolve it.", true, 0.1, true, 1.0 );
+ConVar sv_vote_creation_timer( "sv_vote_creation_timer", "150", FCVAR_NONE, "How long before a player can attempt to call another vote (in seconds)." );
+ConVar sv_vote_quorum_ratio( "sv_vote_quorum_ratio", "0.6", FCVAR_NOTIFY, "The minimum ratio of eligible players needed to pass a vote. Min 0.5, Max 1.0.", true, 0.1f, true, 1.0f );
ConVar sv_vote_allow_spectators( "sv_vote_allow_spectators", "0", FCVAR_NONE, "Allow spectators to vote?" );
ConVar sv_vote_ui_hide_disabled_issues( "sv_vote_ui_hide_disabled_issues", "1", FCVAR_NONE, "Suppress listing of disabled issues in the vote setup screen." );
@@ -61,7 +62,9 @@ public:
CVoteControllerSystem( char const *name ) : CAutoGameSystemPerFrame( name )
{
SetDefLessFunc( m_mapKickWatchList );
+ SetDefLessFunc( m_mapNameLockedList );
m_flNextKickCheckTime = 0.f;
+ m_flNextNameLockCheckTime = 0.f;
}
virtual void LevelInitPreEntity()
@@ -93,38 +96,54 @@ public:
break; // Constantly called code - resume on next pass
}
- CBasePlayer *pTarget = NULL;
- CSteamID steamIDPlayer;
- for ( int j = 1; j <= gpGlobals->maxClients; j++ )
+ CBasePlayer *pTarget = UTIL_PlayerBySteamID( m_mapKickWatchList.Key( i ) );
+ if ( pTarget )
{
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( j );
- if ( !pPlayer )
- continue;
+ // Welcome back
+ engine->ServerCommand( CFmtStr( "kickid %d %s;", pTarget->GetUserID(), "Kicked by server." ) );
+ }
+ }
- if ( pPlayer->GetSteamID( &steamIDPlayer ) == false )
- continue;
+ m_flNextKickCheckTime = gpGlobals->curtime + 0.2f;
+ }
- if ( steamIDPlayer == m_mapKickWatchList.Key( i ) )
+ // Name lock management
+ if ( m_flNextNameLockCheckTime < gpGlobals->curtime )
+ {
+ FOR_EACH_MAP( m_mapNameLockedList, i )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerBySteamID( m_mapNameLockedList.Key( i ) );
+
+ // Time up?
+ if ( gpGlobals->curtime > m_mapNameLockedList[i] )
+ {
+ // Disable the lock if they're still here
+ if ( pPlayer )
{
- pTarget = pPlayer;
- break;
+ engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", pPlayer->GetUserID(), 0 ) );
}
- }
- if ( pTarget )
+ // Remove and break - this will re-run in 1 second
+ m_mapNameLockedList.RemoveAt( i );
+ break;
+ }
+ // See if they reconnected
+ else if ( pPlayer && !engine->IsPlayerNameLocked( pPlayer->edict() ) )
{
- // Welcome back
- engine->ServerCommand( CFmtStr( "kickid %d %s;", pTarget->GetUserID(), "Kicked by server." ) );
+ engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", pPlayer->GetUserID(), 1 ) );
}
}
- m_flNextKickCheckTime = gpGlobals->curtime + 0.2f;
+ m_flNextNameLockCheckTime = gpGlobals->curtime + 1.f;
}
}
}
void AddPlayerToKickWatchList( CSteamID steamID, float flDuration )
{
+ if ( !steamID.IsValid() || !steamID.BIndividualAccount() )
+ return;
+
flDuration = clamp( flDuration, 1.f, (float)k_nKickWatchListMaxDuration );
if ( m_mapKickWatchList.Find( steamID ) == m_mapKickWatchList.InvalidIndex() )
{
@@ -132,10 +151,24 @@ public:
}
}
+ void AddPlayerToNameLockedList( CSteamID steamID, float flDuration )
+ {
+ if ( !steamID.IsValid() || !steamID.BIndividualAccount() )
+ return;
+
+ flDuration = clamp( flDuration, 1.f, (float)k_nKickWatchListMaxDuration );
+ if ( m_mapNameLockedList.Find( steamID ) == m_mapNameLockedList.InvalidIndex() )
+ {
+ m_mapNameLockedList.Insert( steamID, ( gpGlobals->curtime + flDuration ) );
+ }
+ }
+
private:
CUtlMap< CSteamID, float > m_mapKickWatchList;
+ CUtlMap< CSteamID, float > m_mapNameLockedList;
float m_flNextKickCheckTime;
+ float m_flNextNameLockCheckTime;
};
CVoteControllerSystem VoteControllerSystem( "CVoteControllerSystem" );
@@ -185,7 +218,7 @@ CON_COMMAND( callvote, "Start a vote on an issue." )
}
CBasePlayer *pVoteCaller = UTIL_GetCommandClient();
- if( !pVoteCaller )
+ if ( !pVoteCaller )
return;
if ( !sv_vote_allow_spectators.GetBool() )
@@ -197,15 +230,21 @@ CON_COMMAND( callvote, "Start a vote on an issue." )
}
}
- // Prevent spamming commands
-#ifndef _DEBUG
+ if ( g_voteController->IsVoteActive() )
+ {
+ ClientPrint( pVoteCaller, HUD_PRINTCENTER, "#GameUI_vote_failed_vote_in_progress" );
+ return;
+ }
+
+ // Ask the controller if this is allowed
int nCooldown = 0;
- if ( !g_voteController->CanEntityCallVote( pVoteCaller, nCooldown ) )
+ vote_create_failed_t nError = VOTE_FAILED_GENERIC;
+
+ if ( !g_voteController->CanEntityCallVote( pVoteCaller, nCooldown, nError ) )
{
- g_voteController->SendVoteCreationFailedMessage( VOTE_FAILED_RATE_EXCEEDED, pVoteCaller, nCooldown );
+ g_voteController->SendVoteCreationFailedMessage( nError, pVoteCaller, nCooldown );
return;
}
-#endif
// Parameters
char szEmptyDetails[MAX_VOTE_DETAILS_LENGTH];
@@ -214,7 +253,7 @@ CON_COMMAND( callvote, "Start a vote on an issue." )
const char *arg3 = args.ArgC() >= 3 ? args[2] : szEmptyDetails;
// If we don't have any arguments, invoke VoteSetup UI
- if( args.ArgC() < 2 )
+ if ( args.ArgC() < 2 )
{
g_voteController->SetupVote( pVoteCaller->entindex() );
return;
@@ -252,7 +291,7 @@ void CVoteController::ResetData( void )
m_acceptingVotesTimer.Invalidate();
m_executeCommandTimer.Invalidate();
m_iEntityHoldingVote = -1;
- m_iOnlyTeamToVote = TEAM_INVALID;
+ m_iOnlyTeamToVote = TEAM_UNASSIGNED;
m_bIsYesNoVote = true;
for( int voteIndex = 0; voteIndex < ARRAYSIZE( m_nVotesCast ); ++voteIndex )
@@ -290,9 +329,22 @@ int CVoteController::UpdateTransmitState( void )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
+bool CVoteController::IsVoteSystemEnabled( void )
+{
+#ifdef TF_DLL
+ if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
+ return false;
+#endif // TF_DLL
+
+ return sv_allow_votes.GetBool();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
bool CVoteController::CanTeamCastVote( int iTeam ) const
{
- if ( m_iOnlyTeamToVote == TEAM_INVALID )
+ if ( m_iOnlyTeamToVote == TEAM_UNASSIGNED )
return true;
return iTeam == m_iOnlyTeamToVote;
@@ -310,7 +362,7 @@ bool CVoteController::SetupVote( int iEntIndex )
int nIssueCount = 0;
// Passing an nIssueCount of 0 triggers a "Voting disabled on server" message in the setup UI
- if ( sv_allow_votes.GetBool() )
+ if ( IsVoteSystemEnabled() )
{
for( int iIndex = 0; iIndex < m_potentialIssues.Count(); ++iIndex )
{
@@ -330,28 +382,28 @@ bool CVoteController::SetupVote( int iEntIndex )
filter.MakeReliable();
UserMessageBegin( filter, "VoteSetup" );
WRITE_BYTE( nIssueCount );
+ int nMsgSize = 0;
for( int iIndex = 0; iIndex < m_potentialIssues.Count(); ++iIndex )
{
CBaseIssue *pCurrentIssue = m_potentialIssues[iIndex];
if ( pCurrentIssue )
{
- if ( pCurrentIssue->IsEnabled() )
- {
- WRITE_STRING( pCurrentIssue->GetTypeString() );
- }
- else
- {
- // Don't send/display disabled issues when set
- if ( sv_vote_ui_hide_disabled_issues.GetBool() )
- continue;
-
- char szDisabledIssueStr[MAX_COMMAND_LENGTH + 12];
- V_strcpy( szDisabledIssueStr, pCurrentIssue->GetTypeString() );
- V_strcat( szDisabledIssueStr, " (Disabled on Server)", sizeof(szDisabledIssueStr) );
-
- WRITE_STRING( szDisabledIssueStr );
- }
+ // Don't send/display disabled issues when set
+ if ( !pCurrentIssue->IsEnabled() && sv_vote_ui_hide_disabled_issues.GetBool() )
+ continue;
+
+ // Don't exceed MAX_USER_MSG_DATA (hack)
+ nMsgSize += ( V_strlen( pCurrentIssue->GetTypeString() ) + 1 );
+ nMsgSize += ( V_strlen( pCurrentIssue->GetTypeStringLocalized() ) + 1 );
+ ++nMsgSize;
+ Assert( nMsgSize <= MAX_USER_MSG_DATA );
+ if ( nMsgSize > MAX_USER_MSG_DATA )
+ continue;
+
+ WRITE_STRING( pCurrentIssue->GetTypeString() );
+ WRITE_STRING( pCurrentIssue->GetTypeStringLocalized() );
+ WRITE_BYTE( pCurrentIssue->IsEnabled() );
}
}
@@ -368,29 +420,29 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons
// Terrible Hack: Dedicated servers pass 99 as the EntIndex
bool bDedicatedServer = ( iEntIndex == DEDICATED_SERVER ) ? true : false;
- if( !sv_allow_votes.GetBool() )
+ if ( !IsVoteSystemEnabled() )
return false;
// Already running a vote?
- if( IsVoteActive() )
+ if ( IsVoteActive() )
return false;
CBasePlayer *pVoteCaller = UTIL_PlayerByIndex( iEntIndex );
- if( !pVoteCaller && !bDedicatedServer )
+ if ( !pVoteCaller && !bDedicatedServer )
return false;
// Find the issue the user is asking for
- for( int issueIndex = 0; issueIndex < m_potentialIssues.Count(); ++issueIndex )
+ for ( int issueIndex = 0; issueIndex < m_potentialIssues.Count(); ++issueIndex )
{
CBaseIssue *pCurrentIssue = m_potentialIssues[issueIndex];
if ( !pCurrentIssue )
return false;
- if( FStrEq( pszTypeString, pCurrentIssue->GetTypeString() ) )
+ if ( FStrEq( pszTypeString, pCurrentIssue->GetTypeString() ) )
{
vote_create_failed_t nErrorCode = VOTE_FAILED_GENERIC;
int nTime = 0;
- if( pCurrentIssue->CanCallVote( iEntIndex, pszDetailString, nErrorCode, nTime ) )
+ if ( pCurrentIssue->CanCallVote( iEntIndex, pszDetailString, nErrorCode, nTime ) )
{
// Establish a bunch of data on this particular issue
pCurrentIssue->SetIssueDetails( pszDetailString );
@@ -399,14 +451,7 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons
m_iEntityHoldingVote = iEntIndex;
if ( !bDedicatedServer )
{
- if( pCurrentIssue->IsAllyRestrictedVote() )
- {
- m_iOnlyTeamToVote = GetVoterTeam( pVoteCaller );
- }
- else
- {
- m_iOnlyTeamToVote = TEAM_INVALID;
- }
+ m_iOnlyTeamToVote = ( pCurrentIssue->IsTeamRestrictedVote() ) ? GetVoterTeam( pVoteCaller ) : TEAM_UNASSIGNED;
}
// Now get our choices
@@ -442,10 +487,10 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons
// Now the vote handling and UI
m_nPotentialVotes = pCurrentIssue->CountPotentialVoters();
- m_acceptingVotesTimer.Start( sv_vote_timer_duration.GetFloat() );
+ m_acceptingVotesTimer.Start( sv_vote_timer_duration.GetFloat() + random->RandomFloat( -1.f, 1.f ) );
// Force the vote holder to agree with a Yes/No vote
- if ( m_bIsYesNoVote && !bDedicatedServer )
+ if ( pCurrentIssue->IsYesNoVote() && !bDedicatedServer )
{
TryCastVote( iEntIndex, "Option1" );
}
@@ -458,7 +503,7 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons
WRITE_BYTE( m_iEntityHoldingVote );
WRITE_STRING( pCurrentIssue->GetDisplayString() );
WRITE_STRING( pCurrentIssue->GetDetailsString() );
- WRITE_BOOL( m_bIsYesNoVote );
+ WRITE_BOOL( pCurrentIssue->IsYesNoVote() );
MessageEnd();
if ( !bDedicatedServer )
@@ -506,11 +551,7 @@ void CVoteController::SendVoteFailedToPassMessage( vote_create_failed_t nReason
{
Assert( m_potentialIssues[m_iActiveIssueIndex] );
- // See if we have a player target.
- CBasePlayer *pVoteTarget = m_potentialIssues[m_iActiveIssueIndex]->m_hPlayerTarget;
- bool bFakeClient = ( pVoteTarget && ( pVoteTarget->IsFakeClient() || pVoteTarget->IsHLTV() || pVoteTarget->IsReplay() ) );
-
- UTIL_LogPrintf( "Vote failed \"%s %s\" with code %i (proxy: %i) \n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), (int)nReason, bFakeClient );
+ UTIL_LogPrintf( "Vote failed \"%s %s\" with code %i\n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), (int)nReason );
CBroadcastRecipientFilter filter;
filter.MakeReliable();
@@ -526,24 +567,24 @@ void CVoteController::SendVoteFailedToPassMessage( vote_create_failed_t nReason
//-----------------------------------------------------------------------------
CVoteController::TryCastVoteResult CVoteController::TryCastVote( int iEntIndex, const char *pszVoteString )
{
- if( !sv_allow_votes.GetBool() )
+ if ( !IsVoteSystemEnabled() )
return CAST_FAIL_SERVER_DISABLE;
- if( iEntIndex >= ARRAYSIZE( m_nVotesCast ) )
+ if ( iEntIndex >= ARRAYSIZE( m_nVotesCast ) )
return CAST_FAIL_SYSTEM_ERROR;
- if( !IsVoteActive() )
+ if ( !IsVoteActive() )
return CAST_FAIL_NO_ACTIVE_ISSUE;
- if( m_executeCommandTimer.HasStarted() )
+ if ( m_executeCommandTimer.HasStarted() )
return CAST_FAIL_VOTE_CLOSED;
- if( m_potentialIssues[m_iActiveIssueIndex] && m_potentialIssues[m_iActiveIssueIndex]->IsAllyRestrictedVote() )
+ if ( m_potentialIssues[m_iActiveIssueIndex] && m_potentialIssues[m_iActiveIssueIndex]->IsTeamRestrictedVote() )
{
CBaseEntity *pVoteHolder = UTIL_EntityByIndex( m_iEntityHoldingVote );
CBaseEntity *pVoter = UTIL_EntityByIndex( iEntIndex );
- if( ( pVoteHolder == NULL ) || ( pVoter == NULL ) || ( GetVoterTeam( pVoteHolder ) != GetVoterTeam( pVoter ) ) )
+ if ( ( pVoteHolder == NULL ) || ( pVoter == NULL ) || ( GetVoterTeam( pVoteHolder ) != GetVoterTeam( pVoter ) ) )
{
return CAST_FAIL_TEAM_RESTRICTED;
}
@@ -552,7 +593,7 @@ CVoteController::TryCastVoteResult CVoteController::TryCastVote( int iEntIndex,
// Look for a previous vote
int nOldVote = m_nVotesCast[iEntIndex];
#ifndef DEBUG
- if( nOldVote != VOTE_UNCAST )
+ if ( nOldVote != VOTE_UNCAST )
{
return CAST_FAIL_NO_CHANGES;
}
@@ -644,67 +685,54 @@ void CVoteController::VoteControllerThink( void )
}
// Vote time is up - process the result
- if( m_acceptingVotesTimer.HasStarted() && m_acceptingVotesTimer.IsElapsed() )
+ if ( m_acceptingVotesTimer.HasStarted() && m_acceptingVotesTimer.IsElapsed() )
{
m_acceptingVotesTimer.Invalidate();
- int nVoteTally = 0;
- for ( int index = 0; index < MAX_VOTE_OPTIONS; index++ )
- {
- nVoteTally += m_nVoteOptionCount.Get( index );
- }
-
- bool bVotePassed = true;
-
- // for record-keeping
+ // For GC record-keeping
if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() )
{
m_potentialIssues[m_iActiveIssueIndex]->SetYesNoVoteCount( m_nVoteOptionCount[VOTE_OPTION1], m_nVoteOptionCount[VOTE_OPTION2], m_nPotentialVotes );
}
- // Have we exceeded the required ratio of Voted-vs-Abstained?
- if ( nVoteTally >= m_nPotentialVotes * sv_vote_quorum_ratio.GetFloat() )
- {
- int nWinningVoteOption = GetWinningVoteOption();
- Assert( nWinningVoteOption >= 0 && nWinningVoteOption < m_VoteOptions.Count() );
+ bool bVotePassed = false;
- if ( nWinningVoteOption >= 0 && nWinningVoteOption < MAX_VOTE_OPTIONS )
+ if ( GetNumVotesCast() >= ( m_nPotentialVotes * m_potentialIssues[m_iActiveIssueIndex]->GetQuorumRatio() ) )
+ {
+ int nPassingVoteOptionIndex = GetVoteIssueIndexWithHighestCount();
+ if ( nPassingVoteOptionIndex >= 0 && nPassingVoteOptionIndex < MAX_VOTE_OPTIONS )
{
- // YES/NO VOTES
+ // YES/NO VOTES - hard-wired to VOTE_OPTION1 (Yes)
if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() )
{
- // Option1 is Yes
- if ( nWinningVoteOption != VOTE_OPTION1 )
+ if ( nPassingVoteOptionIndex == VOTE_OPTION1 )
{
- SendVoteFailedToPassMessage( VOTE_FAILED_YES_MUST_EXCEED_NO );
- bVotePassed = false;
- }
+ bVotePassed = true;
+ }
}
- // GENERAL VOTES:
- // We set the details string after the vote, since that's when
- // we finally have a parameter to pass along and execute
- else if ( nWinningVoteOption < m_VoteOptions.Count() )
+ // GENERAL VOTES - as long as there's a quorum, go with the most popular choice
+ else
{
- m_potentialIssues[m_iActiveIssueIndex]->SetIssueDetails( m_VoteOptions[nWinningVoteOption] );
+ bVotePassed = true;
+
+ // We set the details string after the vote, since that's when
+ // we finally have a parameter to pass along and execute
+ m_potentialIssues[m_iActiveIssueIndex]->SetIssueDetails( m_VoteOptions[nPassingVoteOptionIndex] );
}
}
}
- else
- {
- SendVoteFailedToPassMessage( VOTE_FAILED_QUORUM_FAILURE );
- bVotePassed = false;
- }
- if ( bVotePassed )
+ if ( bVotePassed )
{
- m_executeCommandTimer.Start( sv_vote_command_delay.GetFloat() );
- m_resetVoteTimer.Start( 5.0 );
-
- // Target is not always a player (changelevel, etc)
+ // Always NULL check, as some votes don't target players (i.e. ChangeLevel)
CBasePlayer *pVoteTarget = m_potentialIssues[m_iActiveIssueIndex]->m_hPlayerTarget;
- bool bFakeClient = ( pVoteTarget && ( pVoteTarget->IsFakeClient() || pVoteTarget->IsHLTV() || pVoteTarget->IsReplay() ) );
- UTIL_LogPrintf( "Vote succeeded \"%s %s\" (proxy: %i) \n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), bFakeClient );
+ // Don't delay successful kick votes
+ float flDelay = IsPlayerBeingKicked( pVoteTarget ) ? 0.f : sv_vote_command_delay.GetFloat();
+ m_executeCommandTimer.Start( flDelay );
+ m_resetVoteTimer.Start( 5.f );
+
+ UTIL_LogPrintf( "Vote succeeded \"%s %s\"\n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString() );
CBroadcastRecipientFilter filter;
filter.MakeReliable();
@@ -717,8 +745,10 @@ void CVoteController::VoteControllerThink( void )
}
else
{
+ vote_create_failed_t nReason = m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ? VOTE_FAILED_YES_MUST_EXCEED_NO : VOTE_FAILED_QUORUM_FAILURE;
+ SendVoteFailedToPassMessage( nReason );
m_potentialIssues[m_iActiveIssueIndex]->OnVoteFailed( m_iEntityHoldingVote );
- m_resetVoteTimer.Start( 5.0 );
+ m_resetVoteTimer.Start( 5.f );
}
}
@@ -768,12 +798,15 @@ void CVoteController::CheckForEarlyVoteClose( void )
//-----------------------------------------------------------------------------
bool CVoteController::IsValidVoter( CBasePlayer *pWhom )
{
- if ( pWhom == NULL )
+ if ( !pWhom )
return false;
if ( !pWhom->IsConnected() )
return false;
+ if ( pWhom->GetTeamNumber() == TEAM_UNASSIGNED )
+ return false;
+
if ( !sv_vote_allow_spectators.GetBool() )
{
if ( pWhom->GetTeamNumber() == TEAM_SPECTATOR )
@@ -818,7 +851,7 @@ void CVoteController::RegisterIssue( CBaseIssue *pszNewIssue )
//-----------------------------------------------------------------------------
void CVoteController::ListIssues( CBasePlayer *pForWhom )
{
- if( !sv_allow_votes.GetBool() )
+ if ( !IsVoteSystemEnabled() )
return;
ClientPrint( pForWhom, HUD_PRINTCONSOLE, "---Vote commands---\n" );
@@ -832,45 +865,34 @@ void CVoteController::ListIssues( CBasePlayer *pForWhom )
}
//-----------------------------------------------------------------------------
-// Purpose:
+// Purpose: -1 when invalid
//-----------------------------------------------------------------------------
-int CVoteController::GetWinningVoteOption( void )
+int CVoteController::GetVoteIssueIndexWithHighestCount( void )
{
- if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() )
+ int nMaxIndex = -1;
+
+ // Legacy Yes/No system
+ if ( m_iActiveIssueIndex != INVALID_ISSUE && m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() )
{
return ( m_nVoteOptionCount[VOTE_OPTION1] > m_nVoteOptionCount[VOTE_OPTION2] ) ? VOTE_OPTION1 : VOTE_OPTION2;
}
+ // Which option had the most votes?
else
{
- CUtlVector <int> pVoteCounts;
+ int nMaxCount = 0;
- // Which option had the most votes?
- // driller: Need to handle ties
- int nHighest = m_nVoteOptionCount[0];
+ // TODO: Handle ties
for ( int iIndex = 0; iIndex < m_nVoteOptionCount.Count(); iIndex ++ )
{
- nHighest = ( ( nHighest < m_nVoteOptionCount[iIndex] ) ? m_nVoteOptionCount[iIndex] : nHighest );
- pVoteCounts.AddToTail( m_nVoteOptionCount[iIndex] );
- }
-
- m_nHighestCountIndex = -1;
- for ( int iIndex = 0; iIndex < m_nVoteOptionCount.Count(); iIndex++ )
- {
- if ( m_nVoteOptionCount[iIndex] == nHighest )
+ if ( m_nVoteOptionCount[iIndex] && m_nVoteOptionCount[iIndex] > nMaxCount )
{
- m_nHighestCountIndex = iIndex;
- // henryg: break on first match, not last. this avoids a crash
- // if we are all tied at zero and we pick something beyond the
- // last vote option. this code really ought to ignore attempts
- // to tally votes for options beyond the last valid one!
- break;
+ nMaxCount = m_nVoteOptionCount[iIndex];
+ nMaxIndex = iIndex;
}
}
-
- return m_nHighestCountIndex;
}
- return -1;
+ return nMaxIndex;
}
//-----------------------------------------------------------------------------
@@ -898,11 +920,12 @@ void CVoteController::TrackVoteCaller( CBasePlayer *pPlayer )
//-----------------------------------------------------------------------------
// Purpose: Check the history of steamIDs that called votes and test against a timer
//-----------------------------------------------------------------------------
-bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown )
+bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown, vote_create_failed_t &nErrorCode )
{
if ( !pPlayer )
return false;
-
+
+#ifndef _DEBUG
CSteamID steamID;
pPlayer->GetSteamID( &steamID );
@@ -913,11 +936,15 @@ bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown )
// Timer elapsed?
nCooldown = (int)( m_VoteCallers[ iIdx ] - gpGlobals->curtime );
if ( nCooldown > 0 )
+ {
+ nErrorCode = VOTE_FAILED_RATE_EXCEEDED;
return false;
+ }
// Expired
m_VoteCallers.Remove( iIdx );
}
+#endif
return true;
};
@@ -925,17 +952,61 @@ bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
+int CVoteController::GetNumVotesCast( void )
+{
+ int nVoteTally = 0;
+
+ for ( int index = 0; index < MAX_VOTE_OPTIONS; index++ )
+ {
+ nVoteTally += m_nVoteOptionCount.Get( index );
+ }
+
+ return nVoteTally;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
void CVoteController::AddPlayerToKickWatchList( CSteamID steamID, float flDuration )
{
VoteControllerSystem.AddPlayerToKickWatchList( steamID, flDuration );
}
//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVoteController::AddPlayerToNameLockedList( CSteamID steamID, float flDuration, int nUserID )
+{
+ engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", nUserID, 1 ) );
+
+ VoteControllerSystem.AddPlayerToNameLockedList( steamID, flDuration );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CVoteController::IsPlayerBeingKicked( CBasePlayer *pPlayer )
+{
+#ifdef TF_DLL
+ if ( pPlayer && m_iActiveIssueIndex != INVALID_ISSUE )
+ {
+ CKickIssue *pKickIssue = dynamic_cast< CKickIssue* >( m_potentialIssues[m_iActiveIssueIndex] );
+ if ( pKickIssue )
+ {
+ return pKickIssue->m_hPlayerTarget == pPlayer;
+ }
+ }
+#endif // TF_DLL
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
// Purpose: BaseIssue
//-----------------------------------------------------------------------------
CBaseIssue::CBaseIssue( const char *pszTypeString )
{
- Q_strcpy( m_szTypeString, pszTypeString );
+ V_strcpy_safe( m_szTypeString, pszTypeString );
m_iNumYesVotes = 0;
m_iNumNoVotes = 0;
@@ -979,13 +1050,13 @@ const char *CBaseIssue::GetDetailsString( void )
//-----------------------------------------------------------------------------
void CBaseIssue::SetIssueDetails( const char *pszDetails )
{
- Q_strcpy( m_szDetailsString, pszDetails );
+ V_strcpy_safe( m_szDetailsString, pszDetails );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
-bool CBaseIssue::IsAllyRestrictedVote( void )
+bool CBaseIssue::IsTeamRestrictedVote( void )
{
return false;
}
@@ -1030,7 +1101,7 @@ void CBaseIssue::OnVoteFailed( int iEntityHoldingVote )
// Need to create a new one
FailedVote *pNewFailedVote = new FailedVote;
int iIndex = m_FailedVotes.AddToTail( pNewFailedVote );
- Q_strcpy( m_FailedVotes[iIndex]->szFailedVoteParameter, GetDetailsString() );
+ V_strcpy_safe( m_FailedVotes[iIndex]->szFailedVoteParameter, GetDetailsString() );
m_FailedVotes[iIndex]->flLockoutTime = gpGlobals->curtime + sv_vote_failure_timer.GetFloat();
}
}
@@ -1184,4 +1255,12 @@ bool CBaseIssue::GetVoteOptions( CUtlVector <const char*> &vecNames )
return true;
}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseIssue::GetQuorumRatio( void )
+{
+ return sv_vote_quorum_ratio.GetFloat();
+}
+