diff options
| author | John Schoenick <[email protected]> | 2015-09-09 18:35:41 -0700 |
|---|---|---|
| committer | John Schoenick <[email protected]> | 2015-09-09 18:35:41 -0700 |
| commit | 0d8dceea4310fde5706b3ce1c70609d72a38efdf (patch) | |
| tree | c831ef32c2c801a5c5a80401736b52c7b5a528ec /mp/src/game/server/vote_controller.cpp | |
| parent | Updated the SDK with the latest code from the TF and HL2 branches. (diff) | |
| download | source-sdk-2013-0d8dceea4310fde5706b3ce1c70609d72a38efdf.tar.xz source-sdk-2013-0d8dceea4310fde5706b3ce1c70609d72a38efdf.zip | |
Diffstat (limited to 'mp/src/game/server/vote_controller.cpp')
| -rw-r--r-- | mp/src/game/server/vote_controller.cpp | 375 |
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(); +} + |