DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }