|
|
//===== 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
|