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

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_voice.h"
  7. #include "fmtstr.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. #if defined( _GAMECONSOLE ) && !defined( _CERT )
  11. ConVar mm_voice_fulldebug( "mm_voice_fulldebug", "0", FCVAR_DEVELOPMENTONLY );
  12. #define MMVOICEMSG(...) if ( mm_voice_fulldebug.GetInt() > 0 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
  13. #define MMVOICEMSG2(...) if ( mm_voice_fulldebug.GetInt() > 1 ) { Msg( "[MMVOICE] " __VA_ARGS__ ); }
  14. #else
  15. #define MMVOICEMSG(...) ((void)0)
  16. #define MMVOICEMSG2(...) ((void)0)
  17. #endif
  18. #if !defined(NO_STEAM) && !defined( SWDS )
  19. static inline bool FriendRelationshipMute( int iRelationship )
  20. {
  21. switch ( iRelationship )
  22. {
  23. case k_EFriendRelationshipBlocked:
  24. case k_EFriendFlagIgnored:
  25. case k_EFriendFlagIgnoredFriend:
  26. return true;
  27. default:
  28. return false;
  29. }
  30. }
  31. #endif
  32. //
  33. // Construction/destruction
  34. //
  35. CMatchVoice::CMatchVoice()
  36. {
  37. ;
  38. }
  39. CMatchVoice::~CMatchVoice()
  40. {
  41. ;
  42. }
  43. static CMatchVoice g_MatchVoice;
  44. CMatchVoice *g_pMatchVoice = &g_MatchVoice;
  45. //
  46. // Implementation
  47. //
  48. // Whether remote player talking can be visualized / audible
  49. bool CMatchVoice::CanPlaybackTalker( XUID xuidTalker )
  50. {
  51. if ( IsMachineMutingLocalTalkers( xuidTalker ) )
  52. {
  53. MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMutingLocalTalkers)\n", xuidTalker );
  54. return false;
  55. }
  56. if ( IsMachineMuted( xuidTalker ) )
  57. {
  58. MMVOICEMSG2( "CanPlaybackTalker(0x%llX)=false(IsMachineMuted)\n", xuidTalker );
  59. return false;
  60. }
  61. return true;
  62. }
  63. // Whether we are explicitly muting a remote player
  64. bool CMatchVoice::IsTalkerMuted( XUID xuidTalker )
  65. {
  66. #if defined( _PS3 ) && !defined( NO_STEAM )
  67. if ( steamapicontext->SteamFriends()->GetUserRestrictions() )
  68. {
  69. MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetUserRestrictions)\n", xuidTalker );
  70. return true;
  71. }
  72. #endif
  73. #if !defined(NO_STEAM) && !defined( SWDS )
  74. if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
  75. {
  76. MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
  77. return true;
  78. }
  79. if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
  80. {
  81. MMVOICEMSG( "IsTalkerMuted(0x%llX)=true(locallist)\n", xuidTalker );
  82. return true;
  83. }
  84. #endif
  85. #if defined( _GAMECONSOLE ) && !defined( _CERT )
  86. XUID xuidOriginal = xuidTalker; xuidOriginal;
  87. #endif
  88. xuidTalker = RemapTalkerXuid( xuidTalker );
  89. #if !defined(NO_STEAM) && !defined( SWDS )
  90. if ( FriendRelationshipMute( steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) ) )
  91. {
  92. MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(GetFriendRelationship=0x%X)\n", xuidTalker, xuidOriginal, steamapicontext->SteamFriends()->GetFriendRelationship( xuidTalker ) );
  93. return true;
  94. }
  95. #endif
  96. #ifdef _X360
  97. if ( MMX360_GetUserCtrlrIndex( xuidTalker ) >= 0 )
  98. // local players are never considered muted locally
  99. return false;
  100. for ( DWORD dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
  101. {
  102. int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
  103. if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
  104. XBX_GetUserIsGuest( iSlot ) )
  105. continue;
  106. BOOL mutedInGuide = false;
  107. if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
  108. mutedInGuide )
  109. {
  110. return true;
  111. }
  112. }
  113. #endif
  114. if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
  115. {
  116. MMVOICEMSG( "IsTalkerMuted(0x%llX/0x%llX)=true(locallist)\n", xuidTalker, xuidOriginal );
  117. return true;
  118. }
  119. return false;
  120. }
  121. // Whether we are muting any player on the player's machine
  122. bool CMatchVoice::IsMachineMuted( XUID xuidPlayer )
  123. {
  124. #ifdef _X360
  125. if ( MMX360_GetUserCtrlrIndex( xuidPlayer ) >= 0 )
  126. // local players are never considered muted locally
  127. return false;
  128. // Find the session and the talker within session members
  129. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  130. if ( !pMatchSession )
  131. return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
  132. KeyValues *pSettings = pMatchSession->GetSessionSettings();
  133. KeyValues *pMachine = NULL;
  134. KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
  135. if ( !pTalker || !pMachine )
  136. return IsTalkerMutedWithPrivileges( -1, xuidPlayer );
  137. // Walk all users from that machine
  138. int numPlayers = pMachine->GetInt( "numPlayers" );
  139. for ( int k = 0; k < numPlayers; ++ k )
  140. {
  141. KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
  142. if ( !pOtherPlayer )
  143. continue;
  144. char const *szOtherName = pOtherPlayer->GetString( "name" );
  145. if ( strchr( szOtherName, '(' ) )
  146. continue;
  147. XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
  148. if ( IsTalkerMutedWithPrivileges( -1, xuidOther ) )
  149. return true;
  150. }
  151. return false;
  152. #else
  153. return IsTalkerMuted( xuidPlayer );
  154. #endif
  155. }
  156. #ifdef _PS3
  157. struct TalkerXuidRemap_t
  158. {
  159. XUID xuidSteamId;
  160. XUID xuidPsnId;
  161. };
  162. #define TALKER_REMAP_CACHE_SIZE 4
  163. static CUtlVector< TalkerXuidRemap_t > g_arrTalkerRemapCache( 0, TALKER_REMAP_CACHE_SIZE );
  164. #endif
  165. // X360: Remap XUID of a player to a valid LIVE-enabled XUID
  166. // PS3: Remap SteamID of a player to a PSN ID
  167. XUID CMatchVoice::RemapTalkerXuid( XUID xuidTalker )
  168. {
  169. if ( !IsGameConsole() )
  170. return xuidTalker;
  171. #ifdef _PS3
  172. for ( int k = 0; k < g_arrTalkerRemapCache.Count(); ++ k )
  173. if ( g_arrTalkerRemapCache[k].xuidSteamId == xuidTalker )
  174. return g_arrTalkerRemapCache[k].xuidPsnId;
  175. #endif
  176. // Find the session and the talker within session members
  177. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  178. if ( !pMatchSession )
  179. return xuidTalker;
  180. KeyValues *pSettings = pMatchSession->GetSessionSettings();
  181. KeyValues *pMachine = NULL;
  182. KeyValues *pTalker = SessionMembersFindPlayer( pSettings, xuidTalker, &pMachine );
  183. if ( !pTalker || !pMachine )
  184. return xuidTalker;
  185. #ifdef _PS3
  186. XUID xuidPsnId = pMachine->GetUint64( "psnid" );
  187. if ( !xuidPsnId )
  188. return xuidTalker;
  189. if ( g_arrTalkerRemapCache.Count() >= TALKER_REMAP_CACHE_SIZE )
  190. g_arrTalkerRemapCache.SetCountNonDestructively( TALKER_REMAP_CACHE_SIZE - 1 );
  191. TalkerXuidRemap_t txr = { xuidTalker, xuidPsnId };
  192. g_arrTalkerRemapCache.AddToHead( txr );
  193. return xuidPsnId;
  194. #endif
  195. // Check this user name if he is a guest
  196. char const *szTalkerName = pTalker->GetString( "name" );
  197. char const *pchr = strchr( szTalkerName, '(' );
  198. if ( !pchr )
  199. return xuidTalker; // user is not a guest
  200. // Find another user from the same machine
  201. int numPlayers = pMachine->GetInt( "numPlayers" );
  202. for ( int k = 0; k < numPlayers; ++ k )
  203. {
  204. KeyValues *pOtherPlayer = pMachine->FindKey( CFmtStr( "player%d", k ) );
  205. if ( !pOtherPlayer )
  206. continue;
  207. char const *szOtherName = pOtherPlayer->GetString( "name" );
  208. if ( strchr( szOtherName, '(' ) )
  209. continue;
  210. XUID xuidOther = pOtherPlayer->GetUint64( "xuid" );
  211. if ( xuidOther )
  212. return xuidOther;
  213. }
  214. // No remapping
  215. return xuidTalker;
  216. }
  217. // Check player-player voice privileges for machine blocking purposes
  218. bool CMatchVoice::IsTalkerMutedWithPrivileges( int dwCtrlr, XUID xuidTalker )
  219. {
  220. #ifdef _X360
  221. if ( -1 == dwCtrlr ) // all controllers should be considered
  222. {
  223. for ( dwCtrlr = 0; dwCtrlr < XUSER_MAX_COUNT; ++ dwCtrlr )
  224. {
  225. if ( IsTalkerMutedWithPrivileges( dwCtrlr, xuidTalker ) )
  226. return true;
  227. }
  228. return false;
  229. }
  230. // Analyze this particular local controller against the given talker
  231. int iSlot = ( XBX_GetNumGameUsers() > 0 ) ? XBX_GetSlotByUserId( dwCtrlr ) : -1;
  232. if ( iSlot >= 0 && iSlot < ( int ) XBX_GetNumGameUsers() &&
  233. XBX_GetUserIsGuest( iSlot ) )
  234. // Guest has no say
  235. return false;
  236. XUSER_SIGNIN_INFO xsi;
  237. if ( ERROR_SUCCESS == XUserGetSigninInfo( dwCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) )
  238. {
  239. if ( xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST )
  240. // LIVE guests have no say
  241. return false;
  242. }
  243. BOOL mutedInGuide = false;
  244. if ( ERROR_SUCCESS == g_pMatchExtensions->GetIXOnline()->XUserMuteListQuery( dwCtrlr, xuidTalker, &mutedInGuide ) &&
  245. mutedInGuide )
  246. {
  247. return true;
  248. }
  249. // Check permissions to see if this player has friends-only or no communication set
  250. // Don't check permissions against other local players
  251. // Check for open privileges
  252. BOOL bHasPrivileges;
  253. DWORD dwResult = XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS, &bHasPrivileges );
  254. if ( dwResult == ERROR_SUCCESS )
  255. {
  256. if ( !bHasPrivileges )
  257. {
  258. // Second call checks for friends-only
  259. XUserCheckPrivilege( dwCtrlr, XPRIVILEGE_COMMUNICATIONS_FRIENDS_ONLY, &bHasPrivileges );
  260. if ( bHasPrivileges )
  261. {
  262. // Privileges are set to friends-only. See if the remote player is on our friends list.
  263. BOOL bIsFriend;
  264. dwResult = XUserAreUsersFriends( dwCtrlr, &xuidTalker, 1, &bIsFriend, NULL );
  265. if ( dwResult != ERROR_SUCCESS || !bIsFriend )
  266. {
  267. return true;
  268. }
  269. }
  270. else
  271. {
  272. // Privilege is nobody, mute them all
  273. return true;
  274. }
  275. }
  276. }
  277. #endif
  278. if ( m_arrMutedTalkers.Find( xuidTalker ) != m_arrMutedTalkers.InvalidIndex() )
  279. {
  280. MMVOICEMSG( "IsTalkerMutedWithPrivileges(%d/0x%llX)=true(locallist)\n", dwCtrlr, xuidTalker );
  281. return true;
  282. }
  283. return false;
  284. }
  285. // Check if player machine is muting any of local players
  286. bool CMatchVoice::IsMachineMutingLocalTalkers( XUID xuidPlayer )
  287. {
  288. // Find the session and the talker within session members
  289. IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession();
  290. if ( !pMatchSession )
  291. return false;
  292. KeyValues *pSettings = pMatchSession->GetSessionSettings();
  293. KeyValues *pMachine = NULL;
  294. SessionMembersFindPlayer( pSettings, xuidPlayer, &pMachine );
  295. if ( !pMachine )
  296. return false;
  297. // Find the local player record in the session
  298. XUID xuidLocalId = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
  299. KeyValues *pLocalMachine = NULL;
  300. SessionMembersFindPlayer( pSettings, xuidLocalId, &pLocalMachine );
  301. if ( !pLocalMachine || pLocalMachine == pMachine )
  302. return false;
  303. int numLocalPlayers = pLocalMachine->GetInt( "numPlayers" );
  304. // Check the mutelist on the machine
  305. if ( KeyValues *pMutelist = pMachine->FindKey( "Mutelist" ) )
  306. {
  307. for ( KeyValues *val = pMutelist->GetFirstValue(); val; val = val->GetNextValue() )
  308. {
  309. XUID xuidMuted = val->GetUint64();
  310. if ( !xuidMuted )
  311. continue;
  312. for ( int iLocal = 0; iLocal < numLocalPlayers; ++ iLocal )
  313. {
  314. XUID xuidLocal = pLocalMachine->GetUint64( CFmtStr( "player%d/xuid", iLocal ) );
  315. if ( xuidMuted == xuidLocal )
  316. {
  317. MMVOICEMSG2( "IsMachineMutingLocalTalkers(0x%llX/0x%llX)=true(mutelist)\n", xuidPlayer, xuidLocal );
  318. return true;
  319. }
  320. }
  321. }
  322. }
  323. return false;
  324. }
  325. // Whether voice recording mode is currently active
  326. bool CMatchVoice::IsVoiceRecording()
  327. {
  328. #if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
  329. #ifdef _PS3
  330. EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 11025 );
  331. #else
  332. EVoiceResult res = steamapicontext->SteamUser()->GetAvailableVoice( NULL, NULL, 0 );
  333. #endif
  334. switch ( res )
  335. {
  336. case k_EVoiceResultOK:
  337. case k_EVoiceResultNoData:
  338. return true;
  339. default:
  340. return false;
  341. }
  342. #endif
  343. return false;
  344. }
  345. // Enable or disable voice recording
  346. void CMatchVoice::SetVoiceRecording( bool bRecordingEnabled )
  347. {
  348. #if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
  349. if ( bRecordingEnabled )
  350. steamapicontext->SteamUser()->StartVoiceRecording();
  351. else
  352. steamapicontext->SteamUser()->StopVoiceRecording();
  353. #endif
  354. }
  355. // Enable or disable voice mute for a given talker
  356. void CMatchVoice::MuteTalker( XUID xuidTalker, bool bMute )
  357. {
  358. #if !defined(_X360) && !defined(NO_STEAM) && !defined( SWDS )
  359. if ( !xuidTalker )
  360. {
  361. if ( !bMute )
  362. m_arrMutedTalkers.Purge();
  363. }
  364. else
  365. {
  366. m_arrMutedTalkers.FindAndFastRemove( xuidTalker );
  367. if ( bMute )
  368. {
  369. m_arrMutedTalkers.AddToTail( xuidTalker );
  370. }
  371. }
  372. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnSysMuteListChanged" ) );
  373. #endif
  374. }
  375. CON_COMMAND( voice_reset_mutelist, "Reset all mute information for all players who were ever muted." )
  376. {
  377. g_pMatchVoice->MuteTalker( 0ull, false );
  378. Msg( "Mute list cleared.\n" );
  379. }
  380. #if !defined( _X360 ) && !defined( NO_STEAM )
  381. CON_COMMAND( voice_mute, "Mute a specific Steam user" )
  382. {
  383. if ( args.ArgC() != 2 )
  384. {
  385. goto usage;
  386. }
  387. else
  388. {
  389. int iUserId = Q_atoi( args.Arg( 1 ) );
  390. player_info_t pi;
  391. if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
  392. {
  393. Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
  394. goto usage;
  395. }
  396. g_pMatchVoice->MuteTalker( pi.xuid, true );
  397. if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
  398. {
  399. Msg( "%s is now muted.\n", pi.name );
  400. }
  401. return;
  402. }
  403. usage:
  404. Msg( "Example usage: voice_mute player# - where player# is a number that you can find with \"voice_show_mute\" command.\n" );
  405. }
  406. CON_COMMAND( voice_unmute, "Unmute a specific Steam user, or `all` to unmute all connected players." )
  407. {
  408. if ( args.ArgC() != 2 )
  409. {
  410. goto usage;
  411. }
  412. else
  413. {
  414. if ( !Q_stricmp( "all", args.Arg(1) ) )
  415. {
  416. XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
  417. int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
  418. for ( int i = 1; i <= maxClients; ++ i )
  419. {
  420. // Get the player info from the engine
  421. player_info_t pi;
  422. if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
  423. continue;
  424. if ( !pi.xuid )
  425. continue;
  426. if ( pi.xuid == xuidLocal )
  427. continue;
  428. g_pMatchVoice->MuteTalker( pi.xuid, false );
  429. }
  430. Msg( "All connected players have been unmuted.\n" );
  431. return;
  432. }
  433. int iUserId = Q_atoi( args.Arg( 1 ) );
  434. player_info_t pi;
  435. if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( iUserId, &pi ) || !pi.xuid )
  436. {
  437. Msg( "Player# is invalid or refers to a bot, please use \"voice_show_mute\" command.\n" );
  438. goto usage;
  439. }
  440. g_pMatchVoice->MuteTalker( pi.xuid, false );
  441. if ( !g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
  442. {
  443. Msg( "%s is now unmuted.\n", pi.name );
  444. }
  445. return;
  446. }
  447. usage:
  448. 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" );
  449. }
  450. CON_COMMAND( voice_show_mute, "Show whether current players are muted." )
  451. {
  452. if ( g_pMatchExtensions->GetIVEngineClient()->GetDemoPlaybackParameters() )
  453. return;
  454. bool bPrinted = false;
  455. XUID xuidLocal = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
  456. int maxClients = g_pMatchExtensions->GetIVEngineClient()->GetMaxClients();
  457. for ( int i = 1; i <= maxClients; ++ i )
  458. {
  459. // Get the player info from the engine
  460. player_info_t pi;
  461. if ( !g_pMatchExtensions->GetIVEngineClient()->GetPlayerInfo( i, &pi ) )
  462. continue;
  463. if ( !pi.xuid )
  464. continue;
  465. if ( pi.xuid == xuidLocal )
  466. continue;
  467. if ( !bPrinted )
  468. {
  469. bPrinted = true;
  470. Msg( "Player# Player Name\n" );
  471. Msg( "------- ----------------\n" );
  472. }
  473. Msg( " % 2d %s %s\n", i, g_pMatchVoice->IsTalkerMuted( pi.xuid ) ? "(muted)" : " ", pi.name );
  474. }
  475. if ( bPrinted )
  476. {
  477. Msg( "------- ----------------\n" );
  478. }
  479. else
  480. {
  481. Msg( "No players currently connected who can be muted.\n" );
  482. }
  483. }
  484. #endif