Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

563 lines
15 KiB

//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_voice.h"
#include "fmtstr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#if defined( _GAMECONSOLE ) && !defined( _CERT )
ConVar mm_voice_fulldebug( "mm_voice_fulldebug", "0", FCVAR_DEVELOPMENTONLY );
#define MMVOICEMSG(...) if ( mm_voice_fulldebug.GetInt() > 0 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
#define MMVOICEMSG2(...) if ( mm_voice_fulldebug.GetInt() > 1 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
#else
#define MMVOICEMSG(...) ((void)0)
#define MMVOICEMSG2(...) ((void)0)
#endif
#if !defined(NO_STEAM) && !defined( SWDS )
static inline bool FriendRelationshipMute( int iRelationship )
{
switch ( iRelationship )
{
case k_EFriendRelationshipBlocked:
case k_EFriendFlagIgnored:
case k_EFriendFlagIgnoredFriend:
return true;
default:
return false;
}
}
#endif
//
// Construction/destruction
//
CMatchVoice::CMatchVoice()
{
;
}
CMatchVoice::~CMatchVoice()
{
;
}
static CMatchVoice g_MatchVoice;
CMatchVoice *g_pMatchVoice = &g_MatchVoice;
//
// Implementation
//
// Whether remote player talking can be visualized / audible
bool CMatchVoice::CanPlaybackTalker( XUID xuidTalker )
{
if ( IsMachineMutingLocalTalkers( xuidTalker ) )
{
MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMutingLocalTalkers)\n", xuidTalker );
return false;
}
if ( IsMachineMuted( xuidTalker ) )
{
MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMuted)\n", xuidTalker );
return false;
}
return true;
}
// Whether we are explicitly muting a remote player
bool CMatchVoice::IsTalkerMuted( XUID xuidTalker )
{
#if defined( _PS3 ) && !defined( NO_STEAM )
if ( steamapicontext->SteamFriends()->GetUserRestrictions() )
{
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetUserRestrictions)\n", xuidTalker );
return true;
}
#endif
#if !defined(NO_STEAM) && !defined( SWDS )
if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
{
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
return true;
}
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
{
MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(locallist)\n", xuidTalker );
return true;
}
#endif
#if defined( _GAMECONSOLE ) && !defined( _CERT )
XUID xuidOriginal = xuidTalker; xuidOriginal;
#endif
xuidTalker = RemapTalkerXuid( xuidTalker );
#if !defined(NO_STEAM) && !defined( SWDS )
if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
{
MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, xuidOriginal, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
return true;
}
#endif
#ifdef _X360
if ( MMX360_GetUserCtrlrIndex( xuidTalker ) >= 0 )
// local players are never considered muted locally
return false;
for ( DWORD dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
{
int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
XBX_GetUserIsGuest( iSlot ) )
continue;
BOOL mutedInGuide = false;
if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
mutedInGuide )
{
return true;
}
}
#endif
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
{
MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(locallist)\n", xuidTalker, xuidOriginal );
return true;
}
return false;
}
// Whether we are muting any player on the player's machine
bool CMatchVoice::IsMachineMuted( XUID xuidPlayer )
{
#ifdef _X360
if ( MMX360_GetUserCtrlrIndex( xuidPlayer ) >= 0 )
// local players are never considered muted locally
return false;
// Find the session and the talker within session members
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
if ( !pMatchSession )
return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
KeyValues *pSettings = pMatchSession->GetSessionSettings();
KeyValues *pMachine = NULL;
KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
if ( !pTalker || !pMachine )
return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
// Walk all users from that machine
int numPlayers = pMachine->GetInt( "numPlayers" );
for ( int k = 0; k < numPlayers; ++ k )
{
KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
if ( !pOtherPlayer )
continue;
char const *szOtherName = pOtherPlayer->GetString( "name" );
if ( strchr( szOtherName, '(' ) )
continue;
XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
if ( IsTalkerMutedWithPrivileges( -1, xuidOther ) )
return true;
}
return false;
#else
return IsTalkerMuted( xuidPlayer );
#endif
}
#ifdef _PS3
struct TalkerXuidRemap_t
{
XUID xuidSteamId;
XUID xuidPsnId;
};
#define TALKER_REMAP_CACHE_SIZE 4
static CUtlVector< TalkerXuidRemap_t > g_arrTalkerRemapCache( 0, TALKER_REMAP_CACHE_SIZE );
#endif
// X360: Remap XUID of a player to a valid LIVE-enabled XUID
// PS3: Remap SteamID of a player to a PSN ID
XUID CMatchVoice::RemapTalkerXuid( XUID xuidTalker )
{
if ( !IsGameConsole() )
return xuidTalker;
#ifdef _PS3
for ( int k = 0; k < g_arrTalkerRemapCache.Count(); ++ k )
if ( g_arrTalkerRemapCache[k].xuidSteamId == xuidTalker )
return g_arrTalkerRemapCache[k].xuidPsnId;
#endif
// Find the session and the talker within session members
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
if ( !pMatchSession )
return xuidTalker;
KeyValues *pSettings = pMatchSession->GetSessionSettings();
KeyValues *pMachine = NULL;
KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidTalker, &pMachine );
if ( !pTalker || !pMachine )
return xuidTalker;
#ifdef _PS3
XUID xuidPsnId = pMachine->GetUint64( "psnid" );
if ( !xuidPsnId )
return xuidTalker;
if ( g_arrTalkerRemapCache.Count() >= TALKER_REMAP_CACHE_SIZE )
g_arrTalkerRemapCache.SetCountNonDestructively( TALKER_REMAP_CACHE_SIZE - 1 );
TalkerXuidRemap_t txr = { xuidTalker, xuidPsnId };
g_arrTalkerRemapCache.AddToHead( txr );
return xuidPsnId;
#endif
// Check this user name if he is a guest
char const *szTalkerName = pTalker->GetString( "name" );
char const *pchr = strchr( szTalkerName, '(' );
if ( !pchr )
return xuidTalker; // user is not a guest
// Find another user from the same machine
int numPlayers = pMachine->GetInt( "numPlayers" );
for ( int k = 0; k < numPlayers; ++ k )
{
KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
if ( !pOtherPlayer )
continue;
char const *szOtherName = pOtherPlayer->GetString( "name" );
if ( strchr( szOtherName, '(' ) )
continue;
XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
if ( xuidOther )
return xuidOther;
}
// No remapping
return xuidTalker;
}
// Check player-player voice privileges for machine blocking purposes
bool CMatchVoice::IsTalkerMutedWithPrivileges( int dwCtrlr, XUID xuidTalker )
{
#ifdef _X360
if ( -1 == dwCtrlr ) // all controllers should be considered
{
for ( dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
{
if ( IsTalkerMutedWithPrivileges( dwCtrlr, xuidTalker ) )
return true;
}
return false;
}
// Analyze this particular local controller against the given talker
int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
XBX_GetUserIsGuest( iSlot ) )
// Guest has no say
return false;
XUSER_SIGNIN_INFO xsi;
if ( ERROR_SUCCESS == XUserGetSigninInfo( dwCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) )
{
if ( xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST )
// LIVE guests have no say
return false;
}
BOOL mutedInGuide = false;
if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
mutedInGuide )
{
return true;
}
// Check permissions to see if this player has friends-only or no communication set
// Don't check permissions against other local players
// Check for open privileges
BOOL bHasPrivileges;
DWORD dwResult = XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS, &bHasPrivileges );
if ( dwResult == ERROR_SUCCESS )
{
if ( !bHasPrivileges )
{
// Second call checks for friends-only
XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS_FRIENDS_ONLY, &bHasPrivileges );
if ( bHasPrivileges )
{
// Privileges are set to friends-only. See if the remote player is on our friends list.
BOOL bIsFriend;
dwResult = XUserAreUsersFriends( dwCtrlr, &xuidTalker, 1, &bIsFriend, NULL );
if ( dwResult != ERROR_SUCCESS || !bIsFriend )
{
return true;
}
}
else
{
// Privilege is nobody, mute them all
return true;
}
}
}
#endif
if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
{
MMVOICEMSG( "IsTalkerMutedWithPrivileges(%d/0x%llX)=true(locallist)\n", dwCtrlr, xuidTalker );
return true;
}
return false;
}
// Check if player machine is muting any of local players
bool CMatchVoice::IsMachineMutingLocalTalkers( XUID xuidPlayer )
{
// Find the session and the talker within session members
IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
if ( !pMatchSession )
return false;
KeyValues *pSettings = pMatchSession->GetSessionSettings();
KeyValues *pMachine = NULL;
SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
if ( !pMachine )
return false;
// Find the local player record in the session
XUID xuidLocalId = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
KeyValues *pLocalMachine = NULL;
SessionMembersFindPlayer( pSettings, xuidLocalId, &pLocalMachine );
if ( !pLocalMachine || pLocalMachine == pMachine )
return false;
int numLocalPlayers = pLocalMachine->GetInt( "numPlayers" );
// Check the mutelist on the machine
if ( KeyValues *pMutelist = pMachine->FindKey( "Mutelist" ) )
{
for ( KeyValues *val = pMutelist->GetFirstValue(); val; val = val->GetNextValue() )
{
XUID xuidMuted = val->GetUint64();
if ( !xuidMuted )
continue;
for ( int iLocal = 0; iLocal < numLocalPlayers; ++ iLocal )
{
XUID xuidLocal = pLocalMachine->GetUint64( CFmtStr( "player%d/xuid", iLocal ) );
if ( xuidMuted == xuidLocal )
{
MMVOICEMSG2( "IsMachineMutingLocalTalkers(0x%llX/0x%llX)=true(mutelist)\n", xuidPlayer, xuidLocal );
return true;
}
}
}
}
return false;
}
// Whether voice recording mode is currently active
bool CMatchVoice::IsVoiceRecording()
{
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
#ifdef _PS3
EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 11025 );
#else
EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 0 );
#endif
switch ( res )
{
case k_EVoiceResultOK:
case k_EVoiceResultNoData:
return true;
default:
return false;
}
#endif
return false;
}
// Enable or disable voice recording
void CMatchVoice::SetVoiceRecording( bool bRecordingEnabled )
{
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
if ( bRecordingEnabled )
steamapicontext->SteamUser()->StartVoiceRecording();
else
steamapicontext->SteamUser()->StopVoiceRecording();
#endif
}
// Enable or disable voice mute for a given talker
void CMatchVoice::MuteTalker( XUID xuidTalker, bool bMute )
{
#if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
if ( !xuidTalker )
{
if ( !bMute )
m_arrMutedTalkers.Purge();
}
else
{
m_arrMutedTalkers.FindAndFastRemove( xuidTalker );
if ( bMute )
{
m_arrMutedTalkers.AddToTail( xuidTalker );
}
}
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnSysMuteListChanged" ) );
#endif
}
CON_COMMAND( voice_reset_mutelist, "Reset all mute information for all players who were ever muted." )
{
g_pMatchVoice->MuteTalker( 0ull, false );
Msg( "Mute list cleared.\n" );
}
#if !defined( _X360 ) && !defined( NO_STEAM )
CON_COMMAND( voice_mute, "Mute a specific Steam user" )
{
if ( args.ArgC() != 2 )
{
goto usage;
}
else
{
int iUserId = Q_atoi( args.Arg( 1 ) );
player_info_t pi;
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
{
Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
goto usage;
}
g_pMatchVoice->MuteTalker( pi.xuid, true );
if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
{
Msg( "%s is now muted.\n", pi.name );
}
return;
}
usage:
Msg( "Example usage: voice_mute player# - where player# is a number that you can find with \"voice_show_mute\" command.\n" );
}
CON_COMMAND( voice_unmute, "Unmute a specific Steam user, or `all` to unmute all connected players." )
{
if ( args.ArgC() != 2 )
{
goto usage;
}
else
{
if ( !Q_stricmp( "all", args.Arg(1) ) )
{
XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
for ( int i = 1; i <= maxClients; ++ i )
{
// Get the player info from the engine
player_info_t pi;
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
continue;
if ( !pi.xuid )
continue;
if ( pi.xuid == xuidLocal )
continue;
g_pMatchVoice->MuteTalker( pi.xuid, false );
}
Msg( "All connected players have been unmuted.\n" );
return;
}
int iUserId = Q_atoi( args.Arg( 1 ) );
player_info_t pi;
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
{
Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
goto usage;
}
g_pMatchVoice->MuteTalker( pi.xuid, false );
if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
{
Msg( "%s is now unmuted.\n", pi.name );
}
return;
}
usage:
Msg( "Example usage: voice_unmute {player#|all} - where player# is a number that you can find with \"voice_show_mute\" command, or all to unmute all connected players.\n" );
}
CON_COMMAND( voice_show_mute, "Show whether current players are muted." )
{
if ( g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
return;
bool bPrinted = false;
XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
for ( int i = 1; i <= maxClients; ++ i )
{
// Get the player info from the engine
player_info_t pi;
if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
continue;
if ( !pi.xuid )
continue;
if ( pi.xuid == xuidLocal )
continue;
if ( !bPrinted )
{
bPrinted = true;
Msg( "Player# Player Name\n" );
Msg( "------- ----------------\n" );
}
Msg( " % 2d %s %s\n", i, g_pMatchVoice->IsTalkerMuted( pi.xuid ) ? "(muted)" : " ", pi.name );
}
if ( bPrinted )
{
Msg( "------- ----------------\n" );
}
else
{
Msg( "No players currently connected who can be muted.\n" );
}
}
#endif