doom3-bfg/neo/sys/sys_voicechat.cpp

670 lines
16 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
2012-11-26 18:58:24 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
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.
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.
===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
2012-11-26 18:58:24 +00:00
#include "sys_voicechat.h"
/*
================================================
idVoiceChatMgr::Init
================================================
*/
void idVoiceChatMgr::Init( void* pXAudio2 )
{
2012-11-26 18:58:24 +00:00
}
/*
================================================
idVoiceChatMgr::Shutdown
================================================
*/
void idVoiceChatMgr::Shutdown()
{
2012-11-26 18:58:24 +00:00
// We shouldn't have voice users if everything shutdown correctly
assert( talkers.Num() == 0 );
assert( remoteMachines.Num() == 0 );
}
/*
================================================
idVoiceChatMgr::RegisterTalker
================================================
*/
void idVoiceChatMgr::RegisterTalker( lobbyUser_t* user, int lobbyType, bool isLocal )
{
2012-11-26 18:58:24 +00:00
int i = FindTalkerIndex( user, lobbyType );
if( !verify( i == -1 ) )
{
2012-11-26 18:58:24 +00:00
assert( talkers[i].lobbyType == lobbyType );
idLib::Printf( "RegisterTalker: Talker already registered.\n" );
return;
}
// Talker not found, need to create a new one
talker_t newTalker;
2012-11-26 18:58:24 +00:00
newTalker.user = user;
newTalker.isLocal = isLocal;
newTalker.lobbyType = lobbyType;
newTalker.registered = false;
newTalker.registeredSuccess = false;
newTalker.machineIndex = -1;
newTalker.groupIndex = 0; // 0 is default group
if( !newTalker.IsLocal() ) // If this is a remote talker, register his machine address
{
2012-11-26 18:58:24 +00:00
newTalker.machineIndex = AddMachine( user->address, lobbyType );
}
2012-11-26 18:58:24 +00:00
talkers.Append( newTalker );
// Since we added a new talker, make sure he is registered. UpdateRegisteredTalkers will catch all users, including this one.
UpdateRegisteredTalkers();
}
/*
================================================
idVoiceChatMgr::UnregisterTalker
================================================
*/
void idVoiceChatMgr::UnregisterTalker( lobbyUser_t* user, int lobbyType, bool isLocal )
{
2012-11-26 18:58:24 +00:00
int i = FindTalkerIndex( user, lobbyType );
if( !verify( i != -1 ) )
{
2012-11-26 18:58:24 +00:00
idLib::Printf( "UnregisterTalker: Talker not found.\n" );
return;
}
talker_t& talker = talkers[i];
2012-11-26 18:58:24 +00:00
assert( talker.IsLocal() == ( talker.machineIndex == -1 ) );
assert( talker.IsLocal() == isLocal );
2012-11-26 18:58:24 +00:00
talker.lobbyType = -1; // Mark for removal
UpdateRegisteredTalkers(); // Make sure the user gets unregistered before we remove him/her
if( talker.machineIndex != -1 )
{
2012-11-26 18:58:24 +00:00
// Unregister the talkers machine (unique address) handle
RemoveMachine( talker.machineIndex, lobbyType );
}
2012-11-26 18:58:24 +00:00
talkers.RemoveIndex( i ); // Finally, remove the talker
}
/*
================================================
idVoiceChatMgr::GetActiveLocalTalkers
================================================
*/
void idVoiceChatMgr::GetActiveLocalTalkers( idStaticList< int, MAX_PLAYERS >& localTalkers )
{
2012-11-26 18:58:24 +00:00
localTalkers.Clear();
for( int i = 0; i < talkers.Num(); i++ )
{
if( !talkers[i].IsLocal() )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( !talkers[i].registeredSuccess )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( !TalkerHasData( i ) )
{
2012-11-26 18:58:24 +00:00
continue;
}
localTalkers.Append( i );
}
}
/*
================================================
idVoiceChatMgr::GetRecipientsForTalker
================================================
*/
void idVoiceChatMgr::GetRecipientsForTalker( int talkerIndex, idStaticList< const lobbyAddress_t*, MAX_PLAYERS >& recipients )
{
2012-11-26 18:58:24 +00:00
recipients.Clear();
talker_t& talker = talkers[talkerIndex];
2012-11-26 18:58:24 +00:00
if( !talker.IsLocal() )
{
2012-11-26 18:58:24 +00:00
return;
}
sendFrame++;
for( int i = 0; i < talkers.Num(); i++ )
{
if( !talkers[i].registeredSuccess )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( talkers[i].IsLocal() )
{
2012-11-26 18:58:24 +00:00
continue; // Only want to send to remote talkers
}
if( !CanSendVoiceTo( talkerIndex, i ) )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( !sendGlobal && talkers[i].groupIndex != activeGroupIndex )
{
2012-11-26 18:58:24 +00:00
continue;
}
2012-11-26 18:58:24 +00:00
assert( talkers[i].machineIndex >= 0 );
remoteMachine_t& remoteMachine = remoteMachines[ talkers[i].machineIndex ];
2012-11-26 18:58:24 +00:00
assert( remoteMachine.refCount > 0 );
assert( remoteMachine.lobbyType == activeLobbyType );
if( remoteMachine.sendFrame == sendFrame )
{
2012-11-26 18:58:24 +00:00
continue; // Already on the recipient list
}
2012-11-26 18:58:24 +00:00
remoteMachine.sendFrame = sendFrame;
2012-11-26 18:58:24 +00:00
recipients.Append( &remoteMachine.address );
}
}
/*
================================================
idVoiceChatMgr::SetTalkerGroup
================================================
*/
void idVoiceChatMgr::SetTalkerGroup( const lobbyUser_t* user, int lobbyType, int groupIndex )
{
2012-11-26 18:58:24 +00:00
int i = FindTalkerIndex( user, lobbyType );
if( !verify( i != -1 ) )
{
2012-11-26 18:58:24 +00:00
idLib::Printf( "SetTalkerGroup: Talker not found.\n" );
return;
}
// Assign the new group index to this talker
talkers[i].groupIndex = groupIndex;
// Since the group index of this player changed, call UpdateRegisteredTalkers, which will register the
2012-11-26 18:58:24 +00:00
// appropriate users based on the current active group and session
UpdateRegisteredTalkers();
}
/*
================================================
idVoiceChatMgr::SetActiveLobby
================================================
*/
void idVoiceChatMgr::SetActiveLobby( int lobbyType )
{
if( activeLobbyType != lobbyType )
{
2012-11-26 18:58:24 +00:00
activeLobbyType = lobbyType;
// When the active session changes, we need to immediately call UpdateRegisteredTalkers,
2012-11-26 18:58:24 +00:00
// which will make sure the appropriate talkers are registered depending on the activeSession.
UpdateRegisteredTalkers();
}
}
/*
================================================
idVoiceChatMgr::SetActiveChatGroup
================================================
*/
void idVoiceChatMgr::SetActiveChatGroup( int groupIndex )
{
if( activeGroupIndex != groupIndex )
{
2012-11-26 18:58:24 +00:00
activeGroupIndex = groupIndex;
// When the active group changes, we need to immediately call UpdateRegisteredTalkers,
2012-11-26 18:58:24 +00:00
// which will make sure the appropriate talkers are registered depending on the activeGroup.
UpdateRegisteredTalkers();
}
}
/*
================================================
idVoiceChatMgr::FindTalkerByUserId
================================================
*/
int idVoiceChatMgr::FindTalkerByUserId( lobbyUserID_t userID, int lobbyType )
{
for( int i = 0; i < talkers.Num(); i++ )
{
if( talkers[i].user->lobbyUserID == userID && talkers[i].lobbyType == lobbyType )
{
2012-11-26 18:58:24 +00:00
return i;
}
}
return -1; // Not found
}
/*
================================================
idVoiceChatMgr::GetLocalChatData
================================================
*/
bool idVoiceChatMgr::GetLocalChatData( int talkerIndex, byte* data, int& dataSize )
{
talker_t& talker = talkers[talkerIndex];
if( !talker.IsLocal() )
{
2012-11-26 18:58:24 +00:00
idLib::Printf( "GetLocalChatData: Talker not local.\n" );
return false; // Talker is remote
}
if( !talker.registeredSuccess )
{
2012-11-26 18:58:24 +00:00
return false;
}
2012-11-26 18:58:24 +00:00
idBitMsg voiceMsg;
voiceMsg.InitWrite( data, dataSize );
2012-11-26 18:58:24 +00:00
talker.user->lobbyUserID.WriteToMsg( voiceMsg );
voiceMsg.WriteByteAlign();
2012-11-26 18:58:24 +00:00
// Remove the size of the userid field from the available buffer size
int voiceDataSize = dataSize - voiceMsg.GetSize();
if( !GetLocalChatDataInternal( talkerIndex, voiceMsg.GetWriteData() + voiceMsg.GetSize(), voiceDataSize ) )
{
2012-11-26 18:58:24 +00:00
dataSize = 0;
return false;
}
2012-11-26 18:58:24 +00:00
dataSize = voiceDataSize + voiceMsg.GetSize();
2012-11-26 18:58:24 +00:00
// Mark the user as talking
talker.talking = true;
talker.talkingTime = Sys_Milliseconds();
2012-11-26 18:58:24 +00:00
return dataSize > 0 ? true : false;
}
/*
================================================
idVoiceChatMgr::SubmitIncomingChatData
================================================
*/
void idVoiceChatMgr::SubmitIncomingChatData( const byte* data, int dataSize )
{
2012-11-26 18:58:24 +00:00
lobbyUserID_t lobbyUserID;
idBitMsg voiceMsg;
voiceMsg.InitRead( data, dataSize );
2012-11-26 18:58:24 +00:00
lobbyUserID.ReadFromMsg( voiceMsg );
voiceMsg.ReadByteAlign();
int i = FindTalkerByUserId( lobbyUserID, activeLobbyType );
if( i == -1 )
{
2012-11-26 18:58:24 +00:00
idLib::Printf( "SubmitIncomingChatData: Talker not found in session.\n" );
return; // Talker is not in this session
}
talker_t& talker = talkers[i];
if( talker.registeredSuccess && !talker.isMuted )
{
2012-11-26 18:58:24 +00:00
// Mark the user as talking
talker.talking = true;
talker.talkingTime = Sys_Milliseconds();
2012-11-26 18:58:24 +00:00
SubmitIncomingChatDataInternal( i, voiceMsg.GetReadData() + voiceMsg.GetReadCount(), voiceMsg.GetRemainingData() );
}
}
/*
========================
idVoiceChatMgr::GetVoiceState
========================
*/
voiceState_t idVoiceChatMgr::GetVoiceState( const lobbyUser_t* user )
{
2012-11-26 18:58:24 +00:00
int i = FindTalkerByUserId( user->lobbyUserID, activeLobbyType );
if( i == -1 )
{
2012-11-26 18:58:24 +00:00
return VOICECHAT_STATE_NO_MIC;
}
talker_t& talker = talkers[i];
if( !talker.hasHeadset )
{
2012-11-26 18:58:24 +00:00
return VOICECHAT_STATE_NO_MIC;
}
if( talker.isMuted )
{
2012-11-26 18:58:24 +00:00
return VOICECHAT_STATE_MUTED_LOCAL;
}
if( talker.talking && Sys_Milliseconds() - talker.talkingTime > 200 )
{
2012-11-26 18:58:24 +00:00
talker.talking = false;
}
return talker.talking ? ( talker.talkingGlobal ? VOICECHAT_STATE_TALKING_GLOBAL : VOICECHAT_STATE_TALKING ) : VOICECHAT_STATE_NOT_TALKING;
2012-11-26 18:58:24 +00:00
}
/*
========================
idVoiceChatMgr::CanSendVoiceTo
========================
*/
bool idVoiceChatMgr::CanSendVoiceTo( int talkerFromIndex, int talkerToIndex )
{
talker_t& talkerFrom = talkers[talkerFromIndex];
if( !talkerFrom.IsLocal() )
{
2012-11-26 18:58:24 +00:00
return false;
}
talker_t& talkerTo = talkers[talkerToIndex];
if( talkerTo.isMuted )
{
2012-11-26 18:58:24 +00:00
return false;
}
2012-11-26 18:58:24 +00:00
return true;
}
/*
========================
idVoiceChatMgr::IsRestrictedByPrivleges
========================
*/
bool idVoiceChatMgr::IsRestrictedByPrivleges()
{
2012-11-26 18:58:24 +00:00
return ( disableVoiceReasons & REASON_PRIVILEGES ) != 0;
}
/*
========================
idVoiceChatMgr::ToggleMuteLocal
========================
*/
void idVoiceChatMgr::ToggleMuteLocal( const lobbyUser_t* src, const lobbyUser_t* target )
{
2012-11-26 18:58:24 +00:00
int fromTalkerIndex = FindTalkerByUserId( src->lobbyUserID, activeLobbyType );
if( fromTalkerIndex == -1 )
{
2012-11-26 18:58:24 +00:00
return;
}
2012-11-26 18:58:24 +00:00
int toTalkerIndex = FindTalkerByUserId( target->lobbyUserID, activeLobbyType );
if( toTalkerIndex == -1 )
{
2012-11-26 18:58:24 +00:00
return;
}
talker_t& targetTalker = talkers[toTalkerIndex];
2012-11-26 18:58:24 +00:00
targetTalker.isMuted = targetTalker.isMuted ? false : true;
}
//================================================
// **** INTERNAL **********
//================================================
/*
================================================
idVoiceChatMgr::FindTalkerIndex
================================================
*/
int idVoiceChatMgr::FindTalkerIndex( const lobbyUser_t* user, int lobbyType )
{
for( int i = 0; i < talkers.Num(); i++ )
{
if( talkers[i].user == user && talkers[i].lobbyType == lobbyType )
{
2012-11-26 18:58:24 +00:00
return i;
}
}
return -1; // Not found
}
/*
================================================
idVoiceChatMgr::FindMachine
================================================
*/
int idVoiceChatMgr::FindMachine( const lobbyAddress_t& address, int lobbyType )
{
for( int i = 0; i < remoteMachines.Num(); i++ )
{
if( remoteMachines[i].refCount == 0 )
{
2012-11-26 18:58:24 +00:00
continue;
}
if( remoteMachines[i].lobbyType == lobbyType && remoteMachines[i].address.Compare( address ) )
{
2012-11-26 18:58:24 +00:00
return i;
}
}
return -1; // Not found
}
/*
================================================
idVoiceChatMgr::AddMachine
================================================
*/
int idVoiceChatMgr::AddMachine( const lobbyAddress_t& address, int lobbyType )
{
2012-11-26 18:58:24 +00:00
int machineIndex = FindMachine( address, lobbyType );
if( machineIndex != -1 )
{
2012-11-26 18:58:24 +00:00
// If we find an existing machine, just increase the ref
remoteMachines[machineIndex].refCount++;
return machineIndex;
}
2012-11-26 18:58:24 +00:00
//
// We didn't find a machine, we'll need to add one
//
2012-11-26 18:58:24 +00:00
// First, see if there is a free machine slot to take
int index = -1;
for( int i = 0; i < remoteMachines.Num(); i++ )
{
if( remoteMachines[i].refCount == 0 )
{
2012-11-26 18:58:24 +00:00
index = i;
break;
}
}
2012-11-26 18:58:24 +00:00
remoteMachine_t newMachine;
2012-11-26 18:58:24 +00:00
newMachine.lobbyType = lobbyType;
newMachine.address = address;
newMachine.refCount = 1;
newMachine.sendFrame = -1;
if( index == -1 )
{
2012-11-26 18:58:24 +00:00
// If we didn't find a machine slot, then add one
index = remoteMachines.Append( newMachine );
}
else
{
2012-11-26 18:58:24 +00:00
// Re-use the machine slot we found
remoteMachines[index] = newMachine;
}
return index;
}
/*
================================================
idVoiceChatMgr::RemoveMachine
================================================
*/
void idVoiceChatMgr::RemoveMachine( int machineIndex, int lobbyType )
{
2012-11-26 18:58:24 +00:00
assert( remoteMachines[machineIndex].refCount > 0 );
assert( remoteMachines[machineIndex].lobbyType == lobbyType );
// Don't remove the machine. refCount will eventually reach 0, which will free up the slot to reclaim.
// We don't want to remove it, because that would invalidate users machineIndex handles into the array
remoteMachines[machineIndex].refCount--;
}
/*
================================================
idVoiceChatMgr::UpdateRegisteredTalkers
================================================
*/
void idVoiceChatMgr::UpdateRegisteredTalkers()
{
for( int pass = 0; pass < 2; pass++ )
{
for( int i = 0; i < talkers.Num(); i++ )
{
talker_t& talker = talkers[i];
2012-11-26 18:58:24 +00:00
bool shouldBeRegistered = ( talker.lobbyType != -1 && disableVoiceReasons == 0 && talker.lobbyType == activeLobbyType );
if( shouldBeRegistered && pass == 0 )
{
2012-11-26 18:58:24 +00:00
continue; // Only unregister on first pass to make room for when the second pass will possibly register new talkers
}
if( talker.registered != shouldBeRegistered )
{
if( !talker.registered )
{
2012-11-26 18:58:24 +00:00
talker.registeredSuccess = RegisterTalkerInternal( i );
}
else if( talker.registeredSuccess )
{
2012-11-26 18:58:24 +00:00
UnregisterTalkerInternal( i );
talker.registeredSuccess = false;
}
2012-11-26 18:58:24 +00:00
talker.registered = shouldBeRegistered;
}
}
}
}
/*
================================================
idVoiceChatMgr::SetDisableVoiceReason
================================================
*/
void idVoiceChatMgr::SetDisableVoiceReason( disableVoiceReason_t reason )
{
if( ( disableVoiceReasons & reason ) == 0 )
{
2012-11-26 18:58:24 +00:00
disableVoiceReasons |= reason;
UpdateRegisteredTalkers();
}
}
/*
================================================
idVoiceChatMgr::ClearDisableVoiceReason
================================================
*/
void idVoiceChatMgr::ClearDisableVoiceReason( disableVoiceReason_t reason )
{
if( ( disableVoiceReasons & reason ) != 0 )
{
2012-11-26 18:58:24 +00:00
disableVoiceReasons &= ~reason;
UpdateRegisteredTalkers();
}
}
/*
================================================
idVoiceChatMgr::SetHeadsetState
================================================
*/
void idVoiceChatMgr::SetHeadsetState( int talkerIndex, bool state )
{
talker_t& talker = talkers[ talkerIndex ];
2012-11-26 18:58:24 +00:00
talker.hasHeadset = state;
}
/*
================================================
idVoiceChatMgr::HasHeadsetStateChanged
================================================
*/
bool idVoiceChatMgr::HasHeadsetStateChanged( int talkerIndex )
{
talker_t& talker = talkers[ talkerIndex ];
2012-11-26 18:58:24 +00:00
// We should only be checking this on the local user
assert( talker.IsLocal() );
bool ret = talker.hasHeadsetChanged;
talker.hasHeadsetChanged = false;
2012-11-26 18:58:24 +00:00
return ret;
}