sys_voicechat.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "sys_voicechat.h"
  23. /*
  24. ================================================
  25. idVoiceChatMgr::Init
  26. ================================================
  27. */
  28. void idVoiceChatMgr::Init( void * pXAudio2 ) {
  29. }
  30. /*
  31. ================================================
  32. idVoiceChatMgr::Shutdown
  33. ================================================
  34. */
  35. void idVoiceChatMgr::Shutdown() {
  36. // We shouldn't have voice users if everything shutdown correctly
  37. assert( talkers.Num() == 0 );
  38. assert( remoteMachines.Num() == 0 );
  39. }
  40. /*
  41. ================================================
  42. idVoiceChatMgr::RegisterTalker
  43. ================================================
  44. */
  45. void idVoiceChatMgr::RegisterTalker( lobbyUser_t * user, int lobbyType, bool isLocal ) {
  46. int i = FindTalkerIndex( user, lobbyType );
  47. if ( !verify( i == -1 ) ) {
  48. assert( talkers[i].lobbyType == lobbyType );
  49. idLib::Printf( "RegisterTalker: Talker already registered.\n" );
  50. return;
  51. }
  52. // Talker not found, need to create a new one
  53. talker_t newTalker;
  54. newTalker.user = user;
  55. newTalker.isLocal = isLocal;
  56. newTalker.lobbyType = lobbyType;
  57. newTalker.registered = false;
  58. newTalker.registeredSuccess = false;
  59. newTalker.machineIndex = -1;
  60. newTalker.groupIndex = 0; // 0 is default group
  61. if ( !newTalker.IsLocal() ) { // If this is a remote talker, register his machine address
  62. newTalker.machineIndex = AddMachine( user->address, lobbyType );
  63. }
  64. talkers.Append( newTalker );
  65. // Since we added a new talker, make sure he is registered. UpdateRegisteredTalkers will catch all users, including this one.
  66. UpdateRegisteredTalkers();
  67. }
  68. /*
  69. ================================================
  70. idVoiceChatMgr::UnregisterTalker
  71. ================================================
  72. */
  73. void idVoiceChatMgr::UnregisterTalker( lobbyUser_t * user, int lobbyType, bool isLocal ) {
  74. int i = FindTalkerIndex( user, lobbyType );
  75. if ( !verify( i != -1 ) ) {
  76. idLib::Printf( "UnregisterTalker: Talker not found.\n" );
  77. return;
  78. }
  79. talker_t & talker = talkers[i];
  80. assert( talker.IsLocal() == ( talker.machineIndex == -1 ) );
  81. assert( talker.IsLocal() == isLocal );
  82. talker.lobbyType = -1; // Mark for removal
  83. UpdateRegisteredTalkers(); // Make sure the user gets unregistered before we remove him/her
  84. if ( talker.machineIndex != -1 ) {
  85. // Unregister the talkers machine (unique address) handle
  86. RemoveMachine( talker.machineIndex, lobbyType );
  87. }
  88. talkers.RemoveIndex( i ); // Finally, remove the talker
  89. }
  90. /*
  91. ================================================
  92. idVoiceChatMgr::GetActiveLocalTalkers
  93. ================================================
  94. */
  95. void idVoiceChatMgr::GetActiveLocalTalkers( idStaticList< int, MAX_PLAYERS > & localTalkers ) {
  96. localTalkers.Clear();
  97. for ( int i = 0; i < talkers.Num(); i++ ) {
  98. if ( !talkers[i].IsLocal() ) {
  99. continue;
  100. }
  101. if ( !talkers[i].registeredSuccess ) {
  102. continue;
  103. }
  104. if ( !TalkerHasData( i ) ) {
  105. continue;
  106. }
  107. localTalkers.Append( i );
  108. }
  109. }
  110. /*
  111. ================================================
  112. idVoiceChatMgr::GetRecipientsForTalker
  113. ================================================
  114. */
  115. void idVoiceChatMgr::GetRecipientsForTalker( int talkerIndex, idStaticList< const lobbyAddress_t *, MAX_PLAYERS > & recipients ) {
  116. recipients.Clear();
  117. talker_t & talker = talkers[talkerIndex];
  118. if ( !talker.IsLocal() ) {
  119. return;
  120. }
  121. sendFrame++;
  122. for ( int i = 0; i < talkers.Num(); i++ ) {
  123. if ( !talkers[i].registeredSuccess ) {
  124. continue;
  125. }
  126. if ( talkers[i].IsLocal() ) {
  127. continue; // Only want to send to remote talkers
  128. }
  129. if ( !CanSendVoiceTo( talkerIndex, i ) ) {
  130. continue;
  131. }
  132. if ( !sendGlobal && talkers[i].groupIndex != activeGroupIndex ) {
  133. continue;
  134. }
  135. assert( talkers[i].machineIndex >= 0 );
  136. remoteMachine_t & remoteMachine = remoteMachines[ talkers[i].machineIndex ];
  137. assert( remoteMachine.refCount > 0 );
  138. assert( remoteMachine.lobbyType == activeLobbyType );
  139. if ( remoteMachine.sendFrame == sendFrame ) {
  140. continue; // Already on the recipient list
  141. }
  142. remoteMachine.sendFrame = sendFrame;
  143. recipients.Append( &remoteMachine.address );
  144. }
  145. }
  146. /*
  147. ================================================
  148. idVoiceChatMgr::SetTalkerGroup
  149. ================================================
  150. */
  151. void idVoiceChatMgr::SetTalkerGroup( const lobbyUser_t * user, int lobbyType, int groupIndex ) {
  152. int i = FindTalkerIndex( user, lobbyType );
  153. if ( !verify( i != -1 ) ) {
  154. idLib::Printf( "SetTalkerGroup: Talker not found.\n" );
  155. return;
  156. }
  157. // Assign the new group index to this talker
  158. talkers[i].groupIndex = groupIndex;
  159. // Since the group index of this player changed, call UpdateRegisteredTalkers, which will register the
  160. // appropriate users based on the current active group and session
  161. UpdateRegisteredTalkers();
  162. }
  163. /*
  164. ================================================
  165. idVoiceChatMgr::SetActiveLobby
  166. ================================================
  167. */
  168. void idVoiceChatMgr::SetActiveLobby( int lobbyType ) {
  169. if ( activeLobbyType != lobbyType ) {
  170. activeLobbyType = lobbyType;
  171. // When the active session changes, we need to immediately call UpdateRegisteredTalkers,
  172. // which will make sure the appropriate talkers are registered depending on the activeSession.
  173. UpdateRegisteredTalkers();
  174. }
  175. }
  176. /*
  177. ================================================
  178. idVoiceChatMgr::SetActiveChatGroup
  179. ================================================
  180. */
  181. void idVoiceChatMgr::SetActiveChatGroup( int groupIndex ) {
  182. if ( activeGroupIndex != groupIndex ) {
  183. activeGroupIndex = groupIndex;
  184. // When the active group changes, we need to immediately call UpdateRegisteredTalkers,
  185. // which will make sure the appropriate talkers are registered depending on the activeGroup.
  186. UpdateRegisteredTalkers();
  187. }
  188. }
  189. /*
  190. ================================================
  191. idVoiceChatMgr::FindTalkerByUserId
  192. ================================================
  193. */
  194. int idVoiceChatMgr::FindTalkerByUserId( lobbyUserID_t userID, int lobbyType ) {
  195. for ( int i = 0; i < talkers.Num(); i++ ) {
  196. if ( talkers[i].user->lobbyUserID == userID && talkers[i].lobbyType == lobbyType ) {
  197. return i;
  198. }
  199. }
  200. return -1; // Not found
  201. }
  202. /*
  203. ================================================
  204. idVoiceChatMgr::GetLocalChatData
  205. ================================================
  206. */
  207. bool idVoiceChatMgr::GetLocalChatData( int talkerIndex, byte * data, int & dataSize ) {
  208. talker_t & talker = talkers[talkerIndex];
  209. if ( !talker.IsLocal() ) {
  210. idLib::Printf( "GetLocalChatData: Talker not local.\n" );
  211. return false; // Talker is remote
  212. }
  213. if ( !talker.registeredSuccess ) {
  214. return false;
  215. }
  216. idBitMsg voiceMsg;
  217. voiceMsg.InitWrite( data, dataSize );
  218. talker.user->lobbyUserID.WriteToMsg( voiceMsg );
  219. voiceMsg.WriteByteAlign();
  220. // Remove the size of the userid field from the available buffer size
  221. int voiceDataSize = dataSize - voiceMsg.GetSize();
  222. if ( !GetLocalChatDataInternal( talkerIndex, voiceMsg.GetWriteData() + voiceMsg.GetSize(), voiceDataSize ) ) {
  223. dataSize = 0;
  224. return false;
  225. }
  226. dataSize = voiceDataSize + voiceMsg.GetSize();
  227. // Mark the user as talking
  228. talker.talking = true;
  229. talker.talkingTime = Sys_Milliseconds();
  230. return dataSize > 0 ? true : false;
  231. }
  232. /*
  233. ================================================
  234. idVoiceChatMgr::SubmitIncomingChatData
  235. ================================================
  236. */
  237. void idVoiceChatMgr::SubmitIncomingChatData( const byte * data, int dataSize ) {
  238. lobbyUserID_t lobbyUserID;
  239. idBitMsg voiceMsg;
  240. voiceMsg.InitRead( data, dataSize );
  241. lobbyUserID.ReadFromMsg( voiceMsg );
  242. voiceMsg.ReadByteAlign();
  243. int i = FindTalkerByUserId( lobbyUserID, activeLobbyType );
  244. if ( i == -1 ) {
  245. idLib::Printf( "SubmitIncomingChatData: Talker not found in session.\n" );
  246. return; // Talker is not in this session
  247. }
  248. talker_t & talker = talkers[i];
  249. if ( talker.registeredSuccess && !talker.isMuted ) {
  250. // Mark the user as talking
  251. talker.talking = true;
  252. talker.talkingTime = Sys_Milliseconds();
  253. SubmitIncomingChatDataInternal( i, voiceMsg.GetReadData() + voiceMsg.GetReadCount(), voiceMsg.GetRemainingData() );
  254. }
  255. }
  256. /*
  257. ========================
  258. idVoiceChatMgr::GetVoiceState
  259. ========================
  260. */
  261. voiceState_t idVoiceChatMgr::GetVoiceState( const lobbyUser_t * user ) {
  262. int i = FindTalkerByUserId( user->lobbyUserID, activeLobbyType );
  263. if ( i == -1 ) {
  264. return VOICECHAT_STATE_NO_MIC;
  265. }
  266. talker_t & talker = talkers[i];
  267. if ( !talker.hasHeadset ) {
  268. return VOICECHAT_STATE_NO_MIC;
  269. }
  270. if ( talker.isMuted ) {
  271. return VOICECHAT_STATE_MUTED_LOCAL;
  272. }
  273. if ( talker.talking && Sys_Milliseconds() - talker.talkingTime > 200 ) {
  274. talker.talking = false;
  275. }
  276. return talker.talking ? (talker.talkingGlobal ? VOICECHAT_STATE_TALKING_GLOBAL : VOICECHAT_STATE_TALKING ) : VOICECHAT_STATE_NOT_TALKING;
  277. }
  278. /*
  279. ========================
  280. idVoiceChatMgr::CanSendVoiceTo
  281. ========================
  282. */
  283. bool idVoiceChatMgr::CanSendVoiceTo( int talkerFromIndex, int talkerToIndex ) {
  284. talker_t & talkerFrom = talkers[talkerFromIndex];
  285. if ( !talkerFrom.IsLocal() ) {
  286. return false;
  287. }
  288. talker_t & talkerTo = talkers[talkerToIndex];
  289. if ( talkerTo.isMuted ) {
  290. return false;
  291. }
  292. return true;
  293. }
  294. /*
  295. ========================
  296. idVoiceChatMgr::IsRestrictedByPrivleges
  297. ========================
  298. */
  299. bool idVoiceChatMgr::IsRestrictedByPrivleges() {
  300. return ( disableVoiceReasons & REASON_PRIVILEGES ) != 0;
  301. }
  302. /*
  303. ========================
  304. idVoiceChatMgr::ToggleMuteLocal
  305. ========================
  306. */
  307. void idVoiceChatMgr::ToggleMuteLocal( const lobbyUser_t * src, const lobbyUser_t * target ) {
  308. int fromTalkerIndex = FindTalkerByUserId( src->lobbyUserID, activeLobbyType );
  309. if ( fromTalkerIndex == -1 ) {
  310. return;
  311. }
  312. int toTalkerIndex = FindTalkerByUserId( target->lobbyUserID, activeLobbyType );
  313. if ( toTalkerIndex == -1 ) {
  314. return;
  315. }
  316. talker_t & targetTalker = talkers[toTalkerIndex];
  317. targetTalker.isMuted = targetTalker.isMuted ? false : true;
  318. }
  319. //================================================
  320. // **** INTERNAL **********
  321. //================================================
  322. /*
  323. ================================================
  324. idVoiceChatMgr::FindTalkerIndex
  325. ================================================
  326. */
  327. int idVoiceChatMgr::FindTalkerIndex( const lobbyUser_t * user, int lobbyType ) {
  328. for ( int i = 0; i < talkers.Num(); i++ ) {
  329. if ( talkers[i].user == user && talkers[i].lobbyType == lobbyType ) {
  330. return i;
  331. }
  332. }
  333. return -1; // Not found
  334. }
  335. /*
  336. ================================================
  337. idVoiceChatMgr::FindMachine
  338. ================================================
  339. */
  340. int idVoiceChatMgr::FindMachine( const lobbyAddress_t & address, int lobbyType ) {
  341. for ( int i = 0; i < remoteMachines.Num(); i++ ) {
  342. if ( remoteMachines[i].refCount == 0 ) {
  343. continue;
  344. }
  345. if ( remoteMachines[i].lobbyType == lobbyType && remoteMachines[i].address.Compare( address ) ) {
  346. return i;
  347. }
  348. }
  349. return -1; // Not found
  350. }
  351. /*
  352. ================================================
  353. idVoiceChatMgr::AddMachine
  354. ================================================
  355. */
  356. int idVoiceChatMgr::AddMachine( const lobbyAddress_t & address, int lobbyType ) {
  357. int machineIndex = FindMachine( address, lobbyType );
  358. if ( machineIndex != -1 ) {
  359. // If we find an existing machine, just increase the ref
  360. remoteMachines[machineIndex].refCount++;
  361. return machineIndex;
  362. }
  363. //
  364. // We didn't find a machine, we'll need to add one
  365. //
  366. // First, see if there is a free machine slot to take
  367. int index = -1;
  368. for ( int i = 0; i < remoteMachines.Num(); i++ ) {
  369. if ( remoteMachines[i].refCount == 0 ) {
  370. index = i;
  371. break;
  372. }
  373. }
  374. remoteMachine_t newMachine;
  375. newMachine.lobbyType = lobbyType;
  376. newMachine.address = address;
  377. newMachine.refCount = 1;
  378. newMachine.sendFrame = -1;
  379. if ( index == -1 ) {
  380. // If we didn't find a machine slot, then add one
  381. index = remoteMachines.Append( newMachine );
  382. } else {
  383. // Re-use the machine slot we found
  384. remoteMachines[index] = newMachine;
  385. }
  386. return index;
  387. }
  388. /*
  389. ================================================
  390. idVoiceChatMgr::RemoveMachine
  391. ================================================
  392. */
  393. void idVoiceChatMgr::RemoveMachine( int machineIndex, int lobbyType ) {
  394. assert( remoteMachines[machineIndex].refCount > 0 );
  395. assert( remoteMachines[machineIndex].lobbyType == lobbyType );
  396. // Don't remove the machine. refCount will eventually reach 0, which will free up the slot to reclaim.
  397. // We don't want to remove it, because that would invalidate users machineIndex handles into the array
  398. remoteMachines[machineIndex].refCount--;
  399. }
  400. /*
  401. ================================================
  402. idVoiceChatMgr::UpdateRegisteredTalkers
  403. ================================================
  404. */
  405. void idVoiceChatMgr::UpdateRegisteredTalkers() {
  406. for ( int pass = 0; pass < 2; pass++ ) {
  407. for ( int i = 0; i < talkers.Num(); i++ ) {
  408. talker_t & talker = talkers[i];
  409. bool shouldBeRegistered = ( talker.lobbyType != -1 && disableVoiceReasons == 0 && talker.lobbyType == activeLobbyType );
  410. if ( shouldBeRegistered && pass == 0 ) {
  411. continue; // Only unregister on first pass to make room for when the second pass will possibly register new talkers
  412. }
  413. if ( talker.registered != shouldBeRegistered ) {
  414. if ( !talker.registered ) {
  415. talker.registeredSuccess = RegisterTalkerInternal( i );
  416. } else if ( talker.registeredSuccess ) {
  417. UnregisterTalkerInternal( i );
  418. talker.registeredSuccess = false;
  419. }
  420. talker.registered = shouldBeRegistered;
  421. }
  422. }
  423. }
  424. }
  425. /*
  426. ================================================
  427. idVoiceChatMgr::SetDisableVoiceReason
  428. ================================================
  429. */
  430. void idVoiceChatMgr::SetDisableVoiceReason( disableVoiceReason_t reason ) {
  431. if ( ( disableVoiceReasons & reason ) == 0 ) {
  432. disableVoiceReasons |= reason;
  433. UpdateRegisteredTalkers();
  434. }
  435. }
  436. /*
  437. ================================================
  438. idVoiceChatMgr::ClearDisableVoiceReason
  439. ================================================
  440. */
  441. void idVoiceChatMgr::ClearDisableVoiceReason( disableVoiceReason_t reason ) {
  442. if ( ( disableVoiceReasons & reason ) != 0 ) {
  443. disableVoiceReasons &= ~reason;
  444. UpdateRegisteredTalkers();
  445. }
  446. }
  447. /*
  448. ================================================
  449. idVoiceChatMgr::SetHeadsetState
  450. ================================================
  451. */
  452. void idVoiceChatMgr::SetHeadsetState( int talkerIndex, bool state ) {
  453. talker_t & talker = talkers[ talkerIndex ];
  454. talker.hasHeadset = state;
  455. }
  456. /*
  457. ================================================
  458. idVoiceChatMgr::HasHeadsetStateChanged
  459. ================================================
  460. */
  461. bool idVoiceChatMgr::HasHeadsetStateChanged( int talkerIndex )
  462. {
  463. talker_t & talker = talkers[ talkerIndex ];
  464. // We should only be checking this on the local user
  465. assert( talker.IsLocal() );
  466. bool ret = talker.hasHeadsetChanged;
  467. talker.hasHeadsetChanged = false;
  468. return ret;
  469. }