//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // // $NoKeywords: $ //============================================================================= #include "voice_gamemgr.h" #include #include #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #define UPDATE_INTERVAL 0.3 // These are stored off as CVoiceGameMgr is created and deleted. CPlayerBitVec g_PlayerModEnable; // Set to 1 for each player if the player wants to use voice in this mod. // (If it's zero, then the server reports that the game rules are saying the // player can't hear anyone). CPlayerBitVec g_BanMasks[VOICE_MAX_PLAYERS]; // Tells which players don't want to hear each other. // These are indexed as clients and each bit represents a client // (so player entity is bit+1). CPlayerBitVec g_SentGameRulesMasks[VOICE_MAX_PLAYERS]; // These store the masks we last sent to each client so we can determine if CPlayerBitVec g_SentBanMasks[VOICE_MAX_PLAYERS]; // we need to resend them. CPlayerBitVec g_bWantModEnable; cvar_t voice_serverdebug = {"voice_serverdebug", "0"}; // Set game rules to allow all clients to talk to each other. // Muted players still can't talk to each other. cvar_t sv_alltalk = {"sv_alltalk", "0"}; // ------------------------------------------------------------------------ // // Static helpers. // ------------------------------------------------------------------------ // // Find a player with a case-insensitive name search. static CBasePlayer* FindPlayerByName( const char *pTestName ) { for( int i = 1; i <= gpGlobals->maxClients; i++ ) { edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( i ); if( pEdict ) { CBaseEntity *pEnt = CBaseEntity::Instance( pEdict ); if( pEnt && pEnt->IsPlayer() ) { const char *pNetName = STRING( pEnt->pev->netname ); if( stricmp( pNetName, pTestName ) == 0 ) { return (CBasePlayer*)pEnt; } } } } return NULL; } static void VoiceServerDebug( char const *pFmt, ... ) { char msg[4096]; va_list marker; if( !voice_serverdebug.value ) return; va_start( marker, pFmt ); _vsnprintf( msg, sizeof(msg), pFmt, marker ); va_end( marker ); ALERT( at_console, "%s", msg ); } // ------------------------------------------------------------------------ // // CVoiceGameMgr. // ------------------------------------------------------------------------ // CVoiceGameMgr::CVoiceGameMgr() { m_UpdateInterval = 0; m_nMaxPlayers = 0; } CVoiceGameMgr::~CVoiceGameMgr() { } bool CVoiceGameMgr::Init( IVoiceGameMgrHelper *pHelper, int maxClients ) { m_pHelper = pHelper; m_nMaxPlayers = VOICE_MAX_PLAYERS < maxClients ? VOICE_MAX_PLAYERS : maxClients; g_engfuncs.pfnPrecacheModel( "sprites/voiceicon.spr" ); m_msgPlayerVoiceMask = REG_USER_MSG( "VoiceMask", VOICE_MAX_PLAYERS_DW * 4 * 2 ); m_msgRequestState = REG_USER_MSG( "ReqState", 0 ); // register voice_serverdebug if it hasn't been registered already if( !CVAR_GET_POINTER( "voice_serverdebug" ) ) CVAR_REGISTER( &voice_serverdebug ); if( !CVAR_GET_POINTER( "sv_alltalk" ) ) CVAR_REGISTER( &sv_alltalk ); return true; } void CVoiceGameMgr::SetHelper( IVoiceGameMgrHelper *pHelper ) { m_pHelper = pHelper; } void CVoiceGameMgr::Update( double frametime ) { // Only update periodically. m_UpdateInterval += frametime; if( m_UpdateInterval < UPDATE_INTERVAL ) return; UpdateMasks(); } void CVoiceGameMgr::ClientConnected( edict_t *pEdict ) { int index = ENTINDEX( pEdict ) - 1; // Clear out everything we use for deltas on this guy. g_bWantModEnable[index] = true; g_SentGameRulesMasks[index].Init( 0 ); g_SentBanMasks[index].Init( 0 ); } // Called to determine if the Receiver has muted (blocked) the Sender // Returns true if the receiver has blocked the sender bool CVoiceGameMgr::PlayerHasBlockedPlayer( CBasePlayer *pReceiver, CBasePlayer *pSender ) { int iReceiverIndex, iSenderIndex; if( !pReceiver || !pSender ) return false; iReceiverIndex = pReceiver->entindex() - 1; iSenderIndex = pSender->entindex() - 1; if( iReceiverIndex < 0 || iReceiverIndex >= m_nMaxPlayers || iSenderIndex < 0 || iSenderIndex >= m_nMaxPlayers ) return false; return ( g_BanMasks[iReceiverIndex][iSenderIndex] ? true : false ); } bool CVoiceGameMgr::ClientCommand( CBasePlayer *pPlayer, const char *cmd ) { int playerClientIndex = pPlayer->entindex() - 1; if( playerClientIndex < 0 || playerClientIndex >= m_nMaxPlayers ) { VoiceServerDebug( "CVoiceGameMgr::ClientCommand: cmd %s from invalid client (%d)\n", cmd, playerClientIndex ); return true; } bool bBan = stricmp( cmd, "vban" ) == 0; if( bBan && CMD_ARGC() >= 2 ) { for( int i = 1; i < CMD_ARGC(); i++ ) { unsigned long mask = 0; sscanf( CMD_ARGV(i), "%lx", &mask ); if( i <= VOICE_MAX_PLAYERS_DW ) { VoiceServerDebug( "CVoiceGameMgr::ClientCommand: vban (0x%x) from %d\n", mask, playerClientIndex ); g_BanMasks[playerClientIndex].SetDWord( i - 1, mask ); } else { VoiceServerDebug( "CVoiceGameMgr::ClientCommand: invalid index (%d)\n", i ); } } // Force it to update the masks now. //UpdateMasks(); return true; } else if( stricmp( cmd, "VModEnable" ) == 0 && CMD_ARGC() >= 2 ) { VoiceServerDebug( "CVoiceGameMgr::ClientCommand: VModEnable (%d)\n", !!atoi( CMD_ARGV( 1 ) ) ); g_PlayerModEnable[playerClientIndex] = !!atoi( CMD_ARGV( 1 ) ); g_bWantModEnable[playerClientIndex] = false; //UpdateMasks(); return true; } else { return false; } } void CVoiceGameMgr::UpdateMasks() { m_UpdateInterval = 0; bool bAllTalk = !!g_engfuncs.pfnCVarGetFloat( "sv_alltalk" ); for( int iClient = 0; iClient < m_nMaxPlayers; iClient++ ) { CBaseEntity *pEnt = UTIL_PlayerByIndex( iClient + 1 ); if( !pEnt || !pEnt->IsPlayer() ) continue; // Request the state of their "VModEnable" cvar. if( g_bWantModEnable[iClient] ) { MESSAGE_BEGIN( MSG_ONE, m_msgRequestState, NULL, pEnt->pev ); MESSAGE_END(); } CBasePlayer *pPlayer = (CBasePlayer*)pEnt; CPlayerBitVec gameRulesMask; if( g_PlayerModEnable[iClient] ) { // Build a mask of who they can hear based on the game rules. for( int iOtherClient = 0; iOtherClient < m_nMaxPlayers; iOtherClient++ ) { pEnt = UTIL_PlayerByIndex( iOtherClient + 1 ); if( pEnt && pEnt->IsPlayer() && ( bAllTalk || m_pHelper->CanPlayerHearPlayer( pPlayer, (CBasePlayer*)pEnt ) ) ) { gameRulesMask[iOtherClient] = true; } } } // If this is different from what the client has, send an update. if( gameRulesMask != g_SentGameRulesMasks[iClient] || g_BanMasks[iClient] != g_SentBanMasks[iClient] ) { g_SentGameRulesMasks[iClient] = gameRulesMask; g_SentBanMasks[iClient] = g_BanMasks[iClient]; MESSAGE_BEGIN( MSG_ONE, m_msgPlayerVoiceMask, NULL, pPlayer->pev ); int dw; for( dw = 0; dw < VOICE_MAX_PLAYERS_DW; dw++ ) { WRITE_LONG( gameRulesMask.GetDWord( dw ) ); WRITE_LONG( g_BanMasks[iClient].GetDWord( dw ) ); } MESSAGE_END(); } // Tell the engine. for( int iOtherClient = 0; iOtherClient < m_nMaxPlayers; iOtherClient++ ) { bool bCanHear = gameRulesMask[iOtherClient] && !g_BanMasks[iClient][iOtherClient]; g_engfuncs.pfnVoice_SetClientListening( iClient + 1, iOtherClient + 1, bCanHear ); } } }