mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2026-03-20 00:49:47 +01:00
Initial commit
This commit is contained in:
593
neo/sys/sys_voicechat.cpp
Normal file
593
neo/sys/sys_voicechat.cpp
Normal file
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 BFG Edition GPL Source Code
|
||||
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
||||
|
||||
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 "../idlib/precompiled.h"
|
||||
#include "sys_voicechat.h"
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::Init
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::Init( void * pXAudio2 ) {
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::Shutdown
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::Shutdown() {
|
||||
|
||||
// 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 ) {
|
||||
|
||||
int i = FindTalkerIndex( user, lobbyType );
|
||||
|
||||
if ( !verify( i == -1 ) ) {
|
||||
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;
|
||||
|
||||
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
|
||||
newTalker.machineIndex = AddMachine( user->address, lobbyType );
|
||||
}
|
||||
|
||||
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 ) {
|
||||
int i = FindTalkerIndex( user, lobbyType );
|
||||
|
||||
if ( !verify( i != -1 ) ) {
|
||||
idLib::Printf( "UnregisterTalker: Talker not found.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
talker_t & talker = talkers[i];
|
||||
|
||||
assert( talker.IsLocal() == ( talker.machineIndex == -1 ) );
|
||||
assert( talker.IsLocal() == isLocal );
|
||||
|
||||
talker.lobbyType = -1; // Mark for removal
|
||||
UpdateRegisteredTalkers(); // Make sure the user gets unregistered before we remove him/her
|
||||
|
||||
if ( talker.machineIndex != -1 ) {
|
||||
// Unregister the talkers machine (unique address) handle
|
||||
RemoveMachine( talker.machineIndex, lobbyType );
|
||||
}
|
||||
|
||||
talkers.RemoveIndex( i ); // Finally, remove the talker
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::GetActiveLocalTalkers
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::GetActiveLocalTalkers( idStaticList< int, MAX_PLAYERS > & localTalkers ) {
|
||||
|
||||
localTalkers.Clear();
|
||||
|
||||
for ( int i = 0; i < talkers.Num(); i++ ) {
|
||||
|
||||
if ( !talkers[i].IsLocal() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !talkers[i].registeredSuccess ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !TalkerHasData( i ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localTalkers.Append( i );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::GetRecipientsForTalker
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::GetRecipientsForTalker( int talkerIndex, idStaticList< const lobbyAddress_t *, MAX_PLAYERS > & recipients ) {
|
||||
|
||||
recipients.Clear();
|
||||
|
||||
talker_t & talker = talkers[talkerIndex];
|
||||
|
||||
if ( !talker.IsLocal() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendFrame++;
|
||||
|
||||
for ( int i = 0; i < talkers.Num(); i++ ) {
|
||||
if ( !talkers[i].registeredSuccess ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( talkers[i].IsLocal() ) {
|
||||
continue; // Only want to send to remote talkers
|
||||
}
|
||||
|
||||
if ( !CanSendVoiceTo( talkerIndex, i ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !sendGlobal && talkers[i].groupIndex != activeGroupIndex ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert( talkers[i].machineIndex >= 0 );
|
||||
|
||||
remoteMachine_t & remoteMachine = remoteMachines[ talkers[i].machineIndex ];
|
||||
|
||||
assert( remoteMachine.refCount > 0 );
|
||||
assert( remoteMachine.lobbyType == activeLobbyType );
|
||||
|
||||
if ( remoteMachine.sendFrame == sendFrame ) {
|
||||
continue; // Already on the recipient list
|
||||
}
|
||||
|
||||
remoteMachine.sendFrame = sendFrame;
|
||||
|
||||
recipients.Append( &remoteMachine.address );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SetTalkerGroup
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SetTalkerGroup( const lobbyUser_t * user, int lobbyType, int groupIndex ) {
|
||||
int i = FindTalkerIndex( user, lobbyType );
|
||||
|
||||
if ( !verify( i != -1 ) ) {
|
||||
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
|
||||
// appropriate users based on the current active group and session
|
||||
UpdateRegisteredTalkers();
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SetActiveLobby
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SetActiveLobby( int lobbyType ) {
|
||||
if ( activeLobbyType != lobbyType ) {
|
||||
activeLobbyType = lobbyType;
|
||||
// When the active session changes, we need to immediately call UpdateRegisteredTalkers,
|
||||
// which will make sure the appropriate talkers are registered depending on the activeSession.
|
||||
UpdateRegisteredTalkers();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SetActiveChatGroup
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SetActiveChatGroup( int groupIndex ) {
|
||||
if ( activeGroupIndex != groupIndex ) {
|
||||
activeGroupIndex = groupIndex;
|
||||
// When the active group changes, we need to immediately call UpdateRegisteredTalkers,
|
||||
// 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 ) {
|
||||
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() ) {
|
||||
idLib::Printf( "GetLocalChatData: Talker not local.\n" );
|
||||
return false; // Talker is remote
|
||||
}
|
||||
|
||||
if ( !talker.registeredSuccess ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
idBitMsg voiceMsg;
|
||||
voiceMsg.InitWrite( data, dataSize );
|
||||
|
||||
talker.user->lobbyUserID.WriteToMsg( voiceMsg );
|
||||
voiceMsg.WriteByteAlign();
|
||||
|
||||
// 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 ) ) {
|
||||
dataSize = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
dataSize = voiceDataSize + voiceMsg.GetSize();
|
||||
|
||||
// Mark the user as talking
|
||||
talker.talking = true;
|
||||
talker.talkingTime = Sys_Milliseconds();
|
||||
|
||||
return dataSize > 0 ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SubmitIncomingChatData
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SubmitIncomingChatData( const byte * data, int dataSize ) {
|
||||
lobbyUserID_t lobbyUserID;
|
||||
|
||||
idBitMsg voiceMsg;
|
||||
voiceMsg.InitRead( data, dataSize );
|
||||
|
||||
lobbyUserID.ReadFromMsg( voiceMsg );
|
||||
voiceMsg.ReadByteAlign();
|
||||
|
||||
int i = FindTalkerByUserId( lobbyUserID, activeLobbyType );
|
||||
|
||||
if ( i == -1 ) {
|
||||
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 ) {
|
||||
// Mark the user as talking
|
||||
talker.talking = true;
|
||||
talker.talkingTime = Sys_Milliseconds();
|
||||
|
||||
SubmitIncomingChatDataInternal( i, voiceMsg.GetReadData() + voiceMsg.GetReadCount(), voiceMsg.GetRemainingData() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
idVoiceChatMgr::GetVoiceState
|
||||
========================
|
||||
*/
|
||||
voiceState_t idVoiceChatMgr::GetVoiceState( const lobbyUser_t * user ) {
|
||||
int i = FindTalkerByUserId( user->lobbyUserID, activeLobbyType );
|
||||
|
||||
if ( i == -1 ) {
|
||||
return VOICECHAT_STATE_NO_MIC;
|
||||
}
|
||||
|
||||
talker_t & talker = talkers[i];
|
||||
|
||||
if ( !talker.hasHeadset ) {
|
||||
return VOICECHAT_STATE_NO_MIC;
|
||||
}
|
||||
|
||||
if ( talker.isMuted ) {
|
||||
return VOICECHAT_STATE_MUTED_LOCAL;
|
||||
}
|
||||
|
||||
if ( talker.talking && Sys_Milliseconds() - talker.talkingTime > 200 ) {
|
||||
talker.talking = false;
|
||||
}
|
||||
|
||||
return talker.talking ? (talker.talkingGlobal ? VOICECHAT_STATE_TALKING_GLOBAL : VOICECHAT_STATE_TALKING ) : VOICECHAT_STATE_NOT_TALKING;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
idVoiceChatMgr::CanSendVoiceTo
|
||||
========================
|
||||
*/
|
||||
bool idVoiceChatMgr::CanSendVoiceTo( int talkerFromIndex, int talkerToIndex ) {
|
||||
talker_t & talkerFrom = talkers[talkerFromIndex];
|
||||
|
||||
if ( !talkerFrom.IsLocal() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
talker_t & talkerTo = talkers[talkerToIndex];
|
||||
|
||||
if ( talkerTo.isMuted ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
idVoiceChatMgr::IsRestrictedByPrivleges
|
||||
========================
|
||||
*/
|
||||
bool idVoiceChatMgr::IsRestrictedByPrivleges() {
|
||||
return ( disableVoiceReasons & REASON_PRIVILEGES ) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
idVoiceChatMgr::ToggleMuteLocal
|
||||
========================
|
||||
*/
|
||||
void idVoiceChatMgr::ToggleMuteLocal( const lobbyUser_t * src, const lobbyUser_t * target ) {
|
||||
int fromTalkerIndex = FindTalkerByUserId( src->lobbyUserID, activeLobbyType );
|
||||
|
||||
if ( fromTalkerIndex == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int toTalkerIndex = FindTalkerByUserId( target->lobbyUserID, activeLobbyType );
|
||||
|
||||
if ( toTalkerIndex == -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
talker_t & targetTalker = talkers[toTalkerIndex];
|
||||
|
||||
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 ) {
|
||||
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 ) {
|
||||
continue;
|
||||
}
|
||||
if ( remoteMachines[i].lobbyType == lobbyType && remoteMachines[i].address.Compare( address ) ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Not found
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::AddMachine
|
||||
================================================
|
||||
*/
|
||||
int idVoiceChatMgr::AddMachine( const lobbyAddress_t & address, int lobbyType ) {
|
||||
|
||||
int machineIndex = FindMachine( address, lobbyType );
|
||||
|
||||
if ( machineIndex != -1 ) {
|
||||
// If we find an existing machine, just increase the ref
|
||||
remoteMachines[machineIndex].refCount++;
|
||||
return machineIndex;
|
||||
}
|
||||
|
||||
//
|
||||
// We didn't find a machine, we'll need to add one
|
||||
//
|
||||
|
||||
// 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 ) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
remoteMachine_t newMachine;
|
||||
|
||||
newMachine.lobbyType = lobbyType;
|
||||
newMachine.address = address;
|
||||
newMachine.refCount = 1;
|
||||
newMachine.sendFrame = -1;
|
||||
|
||||
if ( index == -1 ) {
|
||||
// If we didn't find a machine slot, then add one
|
||||
index = remoteMachines.Append( newMachine );
|
||||
} else {
|
||||
// Re-use the machine slot we found
|
||||
remoteMachines[index] = newMachine;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::RemoveMachine
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::RemoveMachine( int machineIndex, int lobbyType ) {
|
||||
|
||||
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];
|
||||
|
||||
bool shouldBeRegistered = ( talker.lobbyType != -1 && disableVoiceReasons == 0 && talker.lobbyType == activeLobbyType );
|
||||
|
||||
if ( shouldBeRegistered && pass == 0 ) {
|
||||
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 ) {
|
||||
talker.registeredSuccess = RegisterTalkerInternal( i );
|
||||
} else if ( talker.registeredSuccess ) {
|
||||
UnregisterTalkerInternal( i );
|
||||
talker.registeredSuccess = false;
|
||||
}
|
||||
|
||||
talker.registered = shouldBeRegistered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SetDisableVoiceReason
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SetDisableVoiceReason( disableVoiceReason_t reason ) {
|
||||
if ( ( disableVoiceReasons & reason ) == 0 ) {
|
||||
disableVoiceReasons |= reason;
|
||||
UpdateRegisteredTalkers();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::ClearDisableVoiceReason
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::ClearDisableVoiceReason( disableVoiceReason_t reason ) {
|
||||
if ( ( disableVoiceReasons & reason ) != 0 ) {
|
||||
disableVoiceReasons &= ~reason;
|
||||
UpdateRegisteredTalkers();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::SetHeadsetState
|
||||
================================================
|
||||
*/
|
||||
void idVoiceChatMgr::SetHeadsetState( int talkerIndex, bool state ) {
|
||||
talker_t & talker = talkers[ talkerIndex ];
|
||||
|
||||
talker.hasHeadset = state;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================
|
||||
idVoiceChatMgr::HasHeadsetStateChanged
|
||||
================================================
|
||||
*/
|
||||
bool idVoiceChatMgr::HasHeadsetStateChanged( int talkerIndex )
|
||||
{
|
||||
talker_t & talker = talkers[ talkerIndex ];
|
||||
|
||||
// We should only be checking this on the local user
|
||||
assert( talker.IsLocal() );
|
||||
|
||||
bool ret = talker.hasHeadsetChanged;
|
||||
talker.hasHeadsetChanged = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user