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