You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2790 lines
85 KiB
2790 lines
85 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: dvserverengine.cpp
|
|
* Content: Implements the CDirectVoiceServerEngine class.
|
|
*
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 07/18/99 rodtoll Created It
|
|
* 07/22/99 rodtoll Added multiple reader/single writer guard to class
|
|
* Added use of new player information handlers
|
|
* Added additional error checks
|
|
* 07/23/99 rodtoll Completed client/server support
|
|
* Bugfixes to client/server
|
|
* Multicast support to the server
|
|
* 07/26/99 rodtoll Updated to use new method for communicating with
|
|
* the transport.
|
|
* 07/29/99 pnewson Updated call to CInputQueue2 constructor
|
|
* 08/03/99 pnewson Cleanup of Frame and Queue classes
|
|
* 08/03/99 rodtoll Removed async flag from send calls, not needed
|
|
* 08/05/99 rodtoll Fixed locking, was causing deadlock w/DirectPlay
|
|
* 08/10/99 rodtoll Removed TODO pragmas
|
|
* 08/18/99 rodtoll Modified speech transmission behaviour from server.
|
|
* To allow server to distinguish bounced speech from
|
|
* client speech, server now sends SPEECHBOUNCED
|
|
* message types.
|
|
* 08/25/99 rodtoll Fixed speech receive so it records target of audio
|
|
* 08/25/99 rodtoll General Cleanup/Modifications to support new
|
|
* compression sub-system.
|
|
* Added parameters to GetCompression func
|
|
* 08/26/99 rodtoll Added more debug statements & shutdown call on session stop
|
|
* 08/30/99 rodtoll Updated to allow client/server mode Mixing/Multicast sessions
|
|
* 09/01/99 rodtoll Added check for valid pointers in func calls
|
|
* 09/02/99 rodtoll Added checks to handle case no local player created
|
|
* 09/04/99 rodtoll Added new "echo server" mode to the server
|
|
* 09/07/99 rodtoll Implemented SetTransmitTarget and GetTransmitTarget
|
|
* 09/10/99 rodtoll Fixed bug with StartSession parameter validation
|
|
* rodtoll Bug w/check for current status in StartSession
|
|
* 09/14/99 rodtoll Added new SetNotifyMask function
|
|
* rodtoll Updated Iniitalize parameters to take notification masks.
|
|
* rodtoll Implemented notification masks. (Allows user to choose which notifications to receive)
|
|
* 09/15/99 rodtoll Fixed bug with target set proper usage checking and client/server mode checking
|
|
* 09/20/99 rodtoll Added more memory alloc failure checks
|
|
* rodtoll Added error handling for client/server mixing thread
|
|
* rodtoll Updated error handling so when session lost send session lost message
|
|
* rodtoll Tightened checks for valid Notify Masks
|
|
* rodtoll Added proper error checking to SetNotifyMask
|
|
* 09/28/99 rodtoll Fixed StopSession for host migration. Won't send SessionLost messages to players
|
|
* if stopping and host migration is available
|
|
* 10/04/99 rodtoll Added usage of the DVPROTOCOL_VERSION_XXX macros
|
|
* rodtoll Additional documentation/DPFs
|
|
* 10/18/99 rodtoll Fix: Calling Initialize twice doesn't fail
|
|
* 10/20/99 rodtoll Fix: Bug #114114 - StartSession takes invalid parameters
|
|
* 10/25/99 rodtoll Fix: Bug #114684 - GetCaps causes lockup on shutdown
|
|
* rodtoll Fix: Bug #114682 - SetSessionDesc crashes when you specify NULL
|
|
* rodtoll Fix: Bug #114223 - Debug messages being printed at error level when inappropriate
|
|
* 10/29/99 rodtoll Bug #113726 - Integrate Voxware Codecs, updating to use new
|
|
* pluggable codec architecture.
|
|
* 11/17/99 rodtoll Fix: Bug #115538 - dwSize members of > sizeof struct were accepted
|
|
* rodtoll Fix: Bug #115823 - Passing NULL for buffer size in GetCompressionTypes crashes
|
|
* rodtoll Fix: Bug #115827 - Calling SetNotifyMask when there is no callback should fail
|
|
* rodtoll Fix: Bug #116440 - Remove unused flags
|
|
* rodtoll Fix: Bug #117447 - GetTransmitTarget has problems
|
|
* 11/22/99 rodtoll Fixed Initialize() would fail incorrectly
|
|
* 11/23/99 rodtoll Updated Initialize/SetNotifyMask so error checking behaviour is consistant
|
|
* rodtoll Updated GetTransmitTarget to release lock while calling into dplay
|
|
* 11/23/99 rodtoll Split CheckForValid into Group and Player
|
|
* 12/06/99 rodtoll Bumped playback/record threads to time critical priority
|
|
* 12/16/99 rodtoll Fix: Bug #122629 - Host migration broken in unusual configurations.
|
|
* Updated to use new algorithm described in dvprot.h
|
|
* - Generate and added host order IDs to player table messages
|
|
* - Added send of new hostmigrateleave message when shutting down and
|
|
* host should migrate
|
|
* - Sends session lost when shutting down if no one available to take over hosting
|
|
* - Added new player list message
|
|
* - Fixed handling of refusing player connections
|
|
* 01/14/2000 rodtoll Updated to allow multiple targets for a single player
|
|
* rodtoll Updated to use FPM for managing memory for Multicast mode
|
|
* rodtoll Updated to use new callback semantics
|
|
* rodtoll Removed DVFLAGS_SYNC as a valid flag for StartSession/StopSession
|
|
* rodtoll Removed DVMSGID_STARTSESSIONRESULT / DVMSGID_STOPSESSIONRESULT
|
|
* 03/29/2000 rodtoll Updated to use new nametable / more efficient locking
|
|
* rodtoll Modified calls to ConfirmValidEntity to check the nametable instead
|
|
* 04/07/2000 rodtoll Bug #32179 - Prevent registration of > 1 interface
|
|
* rodtoll Updated to use no copy sends, so handles pooling frames to be sent, proper
|
|
* pulling of frames from pools and returns.
|
|
* 05/31/2000 rodtoll Bug #35860 - Fix VC6 compile errors for instrumented builds
|
|
* 06/02/2000 rodtoll Moved host migration so it is keyed off voice message and transport messages.
|
|
* More reliable this way.
|
|
* 06/15/2000 rodtoll Bug #36794 - dwIndex build error with old compiler fixed
|
|
* 06/21/2000 rodtoll Bug #36820 - Host migration failure when local client is next host.
|
|
* Updated shutdown sequence to deregister server before sending out hostmigrateleave
|
|
* 06/27/2000 rodtoll Bug #37604 - Voice objects don't get SESSION_LOST when transport session is closed
|
|
* Added send of session lost message in this case.
|
|
* rodtoll Fixed window which would cause crash when outstanding sends are completed after we
|
|
* have deregistered -- we now wait.
|
|
* 06/28/2000 rodtoll Prefix Bug #38022
|
|
* 07/01/2000 rodtoll Bug #38280 - DVMSGID_DELETEVOICEPLAYER messages are being sent in non-peer to peer sessions
|
|
* 07/09/2000 rodtoll Added signature bytes
|
|
* 07/22/2000 rodtoll Bug #40284 - Initialize() and SetNotifyMask() should return invalidparam instead of invalidpointer
|
|
* rodtoll Bug #40296, 38858 - Crashes due to shutdown race condition
|
|
* Now ensures that all threads from transport have left and that
|
|
* all notificatinos have been processed before shutdown is complete.
|
|
* rodtoll Bug #39586 - Trap 14 in DPVVOX.DLL during session of voicegroup, adding guards for overwrites
|
|
* 07/26/2000 rodtoll Bug #40607 - DPVoice: Problems with Mixing serer when there are more then 3 clients connected to a server and 2 of the transmit
|
|
* Logic bug caused all people reusing mix to get garbage
|
|
* 08/03/2000 rodtoll Bug #41320 - DirectPlayVoice servers will reject connections from newer clients
|
|
* 08/08/2000 rodtoll Was missing a DNLeaveCriticalSection during shutdown
|
|
* 08/25/2000 rodtoll Bug #43485 - Memory leak -- Session Lost case leaks one buffer
|
|
* 08/28/2000 masonb Voice Merge: DNet FPOOLs use DNCRITICAL_SECTION, modified m_pBufferDescPool usage
|
|
* 09/01/2000 masonb Modified Mixer to call _endthread to clean up thread handle
|
|
* 09/14/2000 rodtoll Bug #45001 - DVOICE: AV if client has targetted > 10 players
|
|
* 09/28/2000 rodtoll Fix Again: Bug #45541 - DPVOICE: Client gets DVERR_TIMEOUT message when disconnecting (Server always confirms disconnect)
|
|
* 11/16/2000 rodtoll Bug #40587 - DPVOICE: Mixing server needs to use multi-processors
|
|
* 11/28/2000 rodtoll Bug #47333 - DPVOICE: Server controlled targetting - invalid targets are not removed automatically
|
|
* 04/02/2001 simonpow Bug #354859 - Fixes for PREfast spotted problems. (HRESULT to BOOL casting)
|
|
* 04/06/2001 kareemc Added Voice Defense
|
|
* 04/09/2001 rodtoll WINBUG #364126 - DPVoice : Memory leak when Initializing 2 Voice Servers with same DPlay transport
|
|
* 09/05/2001 simonpow Bug #463972. Added constuct/destruct methods to initialisation
|
|
* calls on m_fpPlayers for CVoicePlayer and CDVCSPlayer
|
|
* 02/28/2002 rodtoll WINBUG #549959 - SECURITY: DPVOICE: Voice server trusts client's target list
|
|
* - Update receive path to use server's copy of client target list when server controlled targetting enabled
|
|
* rodtoll WINBUG #549943 - SECURITY: DPVOICE: Possible corruption of voice server state
|
|
* - Harden receive paths - i.e. ensure host migration processing only when host migration available,
|
|
* prevent double disconnects from crashing server
|
|
* rodtoll WINBUG #550124 - SECURITY: DPVOICE: Shared memory region with NULL DACL
|
|
* - Remove performance statistics dumping to shared memory
|
|
* 03/01/2002 simonpow Bug #549974 Added anti-hack check for incoming packet
|
|
* rate to HandleSpeechWithTarget method
|
|
* 06/13/2002 simonpow BUG #59944 Switched over to using Threadpool based timers rather than multimedia
|
|
***************************************************************************/
|
|
|
|
#include "dxvoicepch.h"
|
|
|
|
|
|
#define SERVER_POOLS_NUM 3
|
|
#define SERVER_POOLS_SIZE_MESSAGE (sizeof(DVPROTOCOLMSG_FULLMESSAGE))
|
|
#define SERVER_POOLS_SIZE_PLAYERLIST DVPROTOCOL_PLAYERLIST_MAXSIZE
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::CDirectVoiceServerEngine"
|
|
//
|
|
// Constructor
|
|
//
|
|
// Initializes the object into the uninitialized state
|
|
//
|
|
CDirectVoiceServerEngine::CDirectVoiceServerEngine( DIRECTVOICESERVEROBJECT *lpObject
|
|
): m_dwSignature(VSIG_SERVERENGINE),
|
|
m_lpMessageHandler(NULL),
|
|
m_lpUserContext(NULL),
|
|
m_lpObject(lpObject),
|
|
m_dvidLocal(0),
|
|
m_dwCurrentState(DVSSTATE_NOTINITIALIZED),
|
|
m_lpSessionTransport(NULL),
|
|
m_lpdwMessageElements(NULL),
|
|
m_dwNumMessageElements(0),
|
|
m_dwNextHostOrderID(0),
|
|
m_pBufferPools(NULL),
|
|
m_pdwBufferPoolSizes(NULL),
|
|
m_hMixerControlThread(NULL),
|
|
m_dwMixerControlThreadID(0),
|
|
m_pStats(NULL),
|
|
m_fCritSecInited(FALSE),
|
|
m_fHostMigrationEnabled(FALSE)
|
|
{
|
|
memset( &m_dvCaps, 0x00, sizeof( DVCAPS ) );
|
|
m_dvCaps.dwSize = sizeof( DVCAPS );
|
|
m_dvCaps.dwFlags = 0;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::InitClass"
|
|
|
|
CDirectVoiceServerEngine::InitClass( )
|
|
{
|
|
if (!DNInitializeCriticalSection( &m_csMixingAddList ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csPlayerActiveList ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csHostOrderLock ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csBufferLock ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csHostOrderLock );
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csClassLock ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csBufferLock );
|
|
DNDeleteCriticalSection( &m_csHostOrderLock );
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csStats ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csClassLock );
|
|
DNDeleteCriticalSection( &m_csBufferLock );
|
|
DNDeleteCriticalSection( &m_csHostOrderLock );
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
if (!DNInitializeCriticalSection( &m_csNotifyLock ))
|
|
{
|
|
DNDeleteCriticalSection( &m_csStats );
|
|
DNDeleteCriticalSection( &m_csClassLock );
|
|
DNDeleteCriticalSection( &m_csBufferLock );
|
|
DNDeleteCriticalSection( &m_csHostOrderLock );
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
return FALSE;
|
|
}
|
|
m_fCritSecInited = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::~CDirectVoiceServerEngine"
|
|
//
|
|
// Destructor
|
|
//
|
|
// Frees the resources associated with the server. Shuts the server down
|
|
// if it is running.
|
|
//
|
|
// Server objects should never be destructed directly, the COM Release
|
|
// method should be used.
|
|
//
|
|
// Called By:
|
|
// DVS_Release (When reference count reaches 0)
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
CDirectVoiceServerEngine::~CDirectVoiceServerEngine()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Stop the session if it's running.
|
|
hr = StopSession(0, FALSE , DV_OK );
|
|
|
|
if( hr != DV_OK && hr != DVERR_NOTHOSTING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "StopSession Failed hr=0x%x", hr );
|
|
}
|
|
|
|
DNEnterCriticalSection( &m_csClassLock );
|
|
|
|
if( m_lpdwMessageElements != NULL )
|
|
delete [] m_lpdwMessageElements;
|
|
|
|
DNLeaveCriticalSection( &m_csClassLock );
|
|
|
|
if (m_fCritSecInited)
|
|
{
|
|
DNDeleteCriticalSection( &m_csMixingAddList );
|
|
DNDeleteCriticalSection( &m_csHostOrderLock );
|
|
DNDeleteCriticalSection( &m_csPlayerActiveList );
|
|
DNDeleteCriticalSection( &m_csBufferLock );
|
|
DNDeleteCriticalSection( &m_csClassLock );
|
|
DNDeleteCriticalSection( &m_csNotifyLock );
|
|
DNDeleteCriticalSection( &m_csStats );
|
|
}
|
|
|
|
m_dwSignature = VSIG_SERVERENGINE_FREE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::TransmitMessage"
|
|
//
|
|
// TransmitMessage
|
|
//
|
|
// This function sends a notification to the notification handler. Before transmitting
|
|
// it, the notify elements are checked to ensure the specified notification is activated.
|
|
// (or notification array is NULL).
|
|
//
|
|
// Called By:
|
|
// - Multiple locations throughout dvsereng.cpp
|
|
//
|
|
// Locks Required:
|
|
// - m_csNotifyLock - The notification lock
|
|
//
|
|
void CDirectVoiceServerEngine::TransmitMessage( DWORD dwMessageType, LPVOID lpdvData, DWORD dwSize )
|
|
{
|
|
if( m_lpMessageHandler != NULL )
|
|
{
|
|
BFCSingleLock slLock( &m_csNotifyLock );
|
|
slLock.Lock();
|
|
|
|
BOOL fSend = FALSE;
|
|
|
|
if( m_dwNumMessageElements == 0 )
|
|
{
|
|
fSend = TRUE;
|
|
}
|
|
else
|
|
{
|
|
for( DWORD dwIndex = 0; dwIndex < m_dwNumMessageElements; dwIndex++ )
|
|
{
|
|
if( m_lpdwMessageElements[dwIndex] == dwMessageType )
|
|
{
|
|
fSend = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( fSend )
|
|
{
|
|
(*m_lpMessageHandler)( m_lpUserContext, dwMessageType,lpdvData );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handles initializing a server object for host migration
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HostMigrateStart"
|
|
//
|
|
// HostMigrateStart
|
|
//
|
|
// This function is called on the object which is to become the new host when
|
|
// the host migrates. It is used instead of the traditional startup to
|
|
// ensure that the object is initialized correctly in the migration case.
|
|
//
|
|
// Called By:
|
|
// - DV_HostMigrate
|
|
//
|
|
// Locks Required:
|
|
// - None
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::HostMigrateStart(LPDVSESSIONDESC lpSessionDesc, DWORD dwHostOrderIDSeed )
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVSE::HostMigrateStart() Begin" );
|
|
|
|
// Start
|
|
hr = StartSession( lpSessionDesc, 0, dwHostOrderIDSeed );
|
|
|
|
// Fail
|
|
if( hr != DV_OK )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "CDirectVoiceServerEngine::HostMigrateStart Failed Hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
hr = InformClientsOfMigrate();
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to inform users of host migration hr=0x%x", hr );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::InformClientsOfMigrate"
|
|
//
|
|
//
|
|
// This function will
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::InformClientsOfMigrate()
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Informing clients of migration" );
|
|
|
|
return Send_HostMigrated();
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::StartSession"
|
|
//
|
|
// StartSession
|
|
//
|
|
// This function handles starting the directplayvoice session in the associated directplay/net
|
|
// session. It also handles startup for a new host when the host migrates. (Called by
|
|
// HostMigrateStart in this case).
|
|
//
|
|
// Called By:
|
|
// - DV_StartSession
|
|
// - HostMigrateStart
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::StartSession(LPDVSESSIONDESC lpSessionDesc, DWORD dwFlags, DWORD dwHostOrderIDSeed )
|
|
{
|
|
HRESULT hr;
|
|
BOOL fPoolsInit = FALSE;
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpSessionDesc = 0x%p dwFlags = 0x%x", lpSessionDesc, dwFlags );
|
|
|
|
if( dwFlags !=0 )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
|
|
return DVERR_INVALIDFLAGS;
|
|
}
|
|
|
|
hr = DV_ValidSessionDesc( lpSessionDesc );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error validating session description. hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
DV_DUMP_SD( lpSessionDesc );
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
|
|
guardLock.Lock();
|
|
|
|
switch( m_dwCurrentState )
|
|
{
|
|
case DVSSTATE_SHUTDOWN:
|
|
case DVSSTATE_RUNNING:
|
|
case DVSSTATE_STARTUP:
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Session is already in progress." );
|
|
return DVERR_HOSTING;
|
|
case DVSSTATE_NOTINITIALIZED:
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
if( m_lpSessionTransport == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid transport" );
|
|
return DVERR_INVALIDOBJECT;
|
|
}
|
|
|
|
m_pTimer = NULL;
|
|
m_hTickSemaphore = NULL;
|
|
m_hShutdownMixerEvent = NULL;
|
|
m_hMixerDoneEvent = NULL;
|
|
m_prWorkerControl = NULL;
|
|
m_pFramePool = NULL;
|
|
|
|
// Retrieve the information about the dplay/dnet session
|
|
hr = m_lpSessionTransport->GetTransportSettings( &m_dwTransportSessionType, &m_dwTransportFlags );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not retrieve transport settings hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
// Peer-to-peer mode not available in client/server mode
|
|
if( m_dwTransportSessionType == DVTRANSPORT_SESSION_CLIENTSERVER &&
|
|
(lpSessionDesc->dwSessionType == DVSESSIONTYPE_PEER) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot host peer session in client/server transport" );
|
|
return DVERR_NOTSUPPORTED;
|
|
}
|
|
|
|
// Server control target not available if host migration is enabled
|
|
if( (m_dwTransportFlags & DVTRANSPORT_MIGRATEHOST) &&
|
|
(lpSessionDesc->dwFlags & DVSESSION_SERVERCONTROLTARGET) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot support host migration with server controlled targetting" );
|
|
return DVERR_NOTSUPPORTED;
|
|
}
|
|
|
|
if( m_dwTransportFlags & DVTRANSPORT_MIGRATEHOST &&
|
|
(lpSessionDesc->dwSessionType == DVSESSIONTYPE_MIXING ||
|
|
lpSessionDesc->dwSessionType == DVSESSIONTYPE_ECHO) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot support host migration for mixing or echo servers" );
|
|
return DVERR_NOTSUPPORTED;
|
|
}
|
|
|
|
hr = DVCDB_GetCompressionInfo( lpSessionDesc->guidCT, &m_lpdvfCompressionInfo );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Compression type not supported. hr=0x%x", hr );
|
|
return DVERR_COMPRESSIONNOTSUPPORTED;
|
|
}
|
|
|
|
m_dwCompressedFrameSize = m_lpdvfCompressionInfo->dwFrameLength;
|
|
|
|
SetCurrentState( DVSSTATE_STARTUP );
|
|
|
|
// Scrub the session description
|
|
|
|
memcpy( &m_dvSessionDesc, lpSessionDesc, sizeof( DVSESSIONDESC ) );
|
|
|
|
// Needs to be here, AFTER the memcpy of the sessiondesc
|
|
m_fHostMigrationEnabled = IsHostMigrationEnabled();
|
|
|
|
if( m_dvSessionDesc.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
|
|
{
|
|
m_dvSessionDesc.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
|
|
}
|
|
|
|
if( m_dvSessionDesc.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
|
|
{
|
|
m_dvSessionDesc.dwBufferQuality = s_dwDefaultBufferQuality;
|
|
}
|
|
|
|
m_dwNextHostOrderID = dwHostOrderIDSeed;
|
|
|
|
m_pStats = &m_statMixingFixed;
|
|
m_pServerStats = &m_dvsServerStatsFixed;
|
|
|
|
// Reset statistics
|
|
ZeroMemory( m_pStats, sizeof( MixingServerStats ) );
|
|
ZeroMemory( m_pServerStats, sizeof( ServerStats ) );
|
|
memcpy( &m_pServerStats->m_dvSessionDesc, &m_dvSessionDesc, sizeof( DVSESSIONDESC ) );
|
|
|
|
hr = SetupBuffers();
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to setup buffer pools hr=0x%x", hr );
|
|
goto STARTSESSION_FAILURE;
|
|
}
|
|
|
|
if( lpSessionDesc->dwSessionType == DVSESSIONTYPE_FORWARDING )
|
|
{
|
|
hr = StartupMulticast();
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to initializing multicast hr=0x%x", hr );
|
|
goto STARTSESSION_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Setup name table
|
|
hr = m_voiceNameTable.Initialize();
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to initialize nametable hr=0x%x", hr );
|
|
goto STARTSESSION_FAILURE;
|
|
}
|
|
|
|
// Initialize player allocation pools
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
fPoolsInit = m_fpPlayers.Initialize(sizeof(CDVCSPlayer),
|
|
CDVCSPlayer::PoolAllocFunction, NULL, NULL, CDVCSPlayer::PoolDeallocFunction);
|
|
}
|
|
else
|
|
{
|
|
fPoolsInit = m_fpPlayers.Initialize(sizeof(CVoicePlayer),
|
|
CVoicePlayer::PoolAllocFunction, NULL, NULL, CVoicePlayer::PoolDeallocFunction);
|
|
}
|
|
|
|
m_blPlayerActiveList.Initialize();
|
|
|
|
SetCurrentState( DVSSTATE_RUNNING );
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
hr = StartupClientServer();
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to startup client/server hr=0x%x", hr );
|
|
goto STARTSESSION_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Tell DirectPlay we're alive and want to see incoming traffic
|
|
hr = m_lpSessionTransport->EnableReceiveHook( m_lpObject, DVTRANSPORT_OBJECTTYPE_SERVER );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed on call to EnableReceiveHook hr=0x%x", hr );
|
|
goto STARTSESSION_FAILURE;
|
|
}
|
|
|
|
STARTSESSION_RETURN:
|
|
|
|
guardLock.Unlock();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Success" );
|
|
|
|
return hr;
|
|
|
|
STARTSESSION_FAILURE:
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
ShutdownClientServer();
|
|
}
|
|
else if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
|
|
{
|
|
ShutdownMulticast();
|
|
}
|
|
|
|
m_voiceNameTable.DeInitialize(TRUE, m_lpUserContext,m_lpMessageHandler);
|
|
|
|
// Initialize player allocation pools
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
delete m_pFramePool;
|
|
m_pFramePool = NULL;
|
|
}
|
|
if( fPoolsInit )
|
|
{
|
|
m_fpPlayers.DeInitialize();
|
|
}
|
|
|
|
FreeBuffers();
|
|
|
|
SetCurrentState( DVSSTATE_IDLE );
|
|
|
|
goto STARTSESSION_RETURN;
|
|
|
|
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::CheckForMigrate"
|
|
BOOL CDirectVoiceServerEngine::CheckForMigrate( DWORD dwFlags, BOOL fSilent )
|
|
{
|
|
// We should shutdown the session if:
|
|
//
|
|
// 1. We're not in peer to peer mode
|
|
// 2. "No host migration" flag was specified in session description
|
|
// 3. "No host migrate" flag was specified on call to StopSession
|
|
// 4. We were not asked to be silent
|
|
// 5. There isn't a host migrate
|
|
//
|
|
if( (m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER ) ||
|
|
(m_dvSessionDesc.dwFlags & DVSESSION_NOHOSTMIGRATION) ||
|
|
(dwFlags & DVFLAGS_NOHOSTMIGRATE) ||
|
|
fSilent ||
|
|
!(m_dwTransportFlags & DVTRANSPORT_MIGRATEHOST) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Destroying session." );
|
|
return FALSE;
|
|
}
|
|
// A migration is possible
|
|
else
|
|
{
|
|
return TRUE;
|
|
|
|
/*
|
|
THIS IS DISABLED BECAUSE OF a race condition where the server has not yet
|
|
received notifications from all players in the session and therefore may
|
|
think (incorrectly) that there is no one left.
|
|
|
|
// We look for at least one client who hasn't marked themselves
|
|
// as notpeerhost
|
|
//
|
|
|
|
DVID dvidNewHost;
|
|
|
|
DWORD dwNewHostOrder = m_voiceNameTable.GetLowestHostOrderID(&dvidNewHost);
|
|
|
|
if( dwNewHostOrder != DVPROTOCOL_HOSTORDER_INVALID )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Found at least one client who can become host. (It's 0x%x)", dvidNewHost );
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Could not find a client to become host" );
|
|
return FALSE;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::StopSession"
|
|
//
|
|
// StopSession
|
|
//
|
|
// This function is responsible for shutting down the directplayvoice session. In sessions
|
|
// without host migration, this function will simply cleanup the memory and detach itself
|
|
// from directplay. This way directplay will take care of the host migration. To use this
|
|
// option, specify fSilent = TRUE.
|
|
//
|
|
// In addition, this function is called when an fatal error occurs while the session is
|
|
// running.
|
|
//
|
|
// It is also called when the user calls StopSession.
|
|
//
|
|
// Called By:
|
|
// - HandleMixerThreadError
|
|
// - HandleStopTransportSession
|
|
// - DVS_StopSession
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::StopSession(DWORD dwFlags, BOOL fSilent, HRESULT hrResult )
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVSE::StopSession() Begin" );
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: dwFlags = 0x%x", dwFlags );
|
|
|
|
|
|
if( dwFlags & ~(DVFLAGS_NOHOSTMIGRATE) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
|
|
return DVERR_INVALIDFLAGS;
|
|
}
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
|
|
guardLock.Lock();
|
|
|
|
// We need to be initialized
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Session is not running" );
|
|
return DVERR_NOTHOSTING;
|
|
}
|
|
|
|
// If no host migrate has been specified
|
|
if( dwFlags & DVFLAGS_NOHOSTMIGRATE )
|
|
m_fHostMigrationEnabled = FALSE;
|
|
|
|
// Stop client/server thread
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Shutting down client/server" );
|
|
ShutdownClientServer();
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Shutting down client/server" );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Waiting for buffer returns." );
|
|
// Wait until all the outstanding sends have completed
|
|
WaitForBufferReturns();
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Waiting for buffer returns." );
|
|
|
|
// Disable receives
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Unhook transport." );
|
|
m_lpSessionTransport->DisableReceiveHook( );
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Unhook transport." );
|
|
|
|
// Waits for transport threads to be done inside our layer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Awaiting detach." );
|
|
m_lpSessionTransport->WaitForDetachCompletion();
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Awaiting detach." );
|
|
|
|
|
|
|
|
// Check to see if there is migration going on. If a migration should
|
|
// happen then we do not transmit a session lost message
|
|
//
|
|
if( !FAILED( hrResult ) )
|
|
{
|
|
if( !CheckForMigrate( dwFlags, fSilent ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Host migration not possible. Sending lost session." );
|
|
Send_SessionLost( DVERR_SESSIONLOST );
|
|
}
|
|
// Host is migrating. Inform the users
|
|
else if( m_dwTransportFlags & DVTRANSPORT_MIGRATEHOST )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Host migration begin." );
|
|
Send_HostMigrateLeave();
|
|
}
|
|
}
|
|
|
|
SetCurrentState( DVSSTATE_SHUTDOWN );
|
|
|
|
// Disable sends
|
|
//m_lpSessionTransport->DestroyTransport();
|
|
|
|
// Kill name table
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Destroying nametable" );
|
|
m_voiceNameTable.DeInitialize(TRUE,m_lpUserContext,m_lpMessageHandler);
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Destroying nametable" );
|
|
|
|
// Cleanup the active list
|
|
CleanupActiveList();
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Cleanup mixing" );
|
|
|
|
delete m_pFramePool;
|
|
m_pFramePool = NULL;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Cleanup.." );
|
|
}
|
|
|
|
// Kill the FPM
|
|
m_fpPlayers.DeInitialize();
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Shutdown forwarding" );
|
|
ShutdownMulticast();
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Shutdown forwarding" );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Final cleanup" );
|
|
FreeBuffers();
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Final cleanup" );
|
|
|
|
m_fHostMigrationEnabled = FALSE;
|
|
|
|
SetCurrentState( DVSSTATE_IDLE );
|
|
|
|
guardLock.Unlock();
|
|
|
|
// Check to see if the transport session was closed
|
|
if( hrResult == DVERR_SESSIONLOST )
|
|
{
|
|
DVMSG_SESSIONLOST dvMsgLost;
|
|
dvMsgLost.dwSize = sizeof( DVMSG_SESSIONLOST );
|
|
dvMsgLost.hrResult = hrResult;
|
|
|
|
TransmitMessage( DVMSGID_SESSIONLOST, &dvMsgLost, sizeof( DVPROTOCOLMSG_SESSIONLOST ) );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return DV_OK;
|
|
}
|
|
|
|
// WaitForBufferReturns
|
|
//
|
|
// This function waits until oustanding sends have completed before continuing
|
|
// we use this to ensure we don't deregister with outstanding sends.
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::WaitForBufferReturns"
|
|
void CDirectVoiceServerEngine::WaitForBufferReturns()
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Waiting for buffer returns. %d remaining", m_BufferDescPool.GetInUseCount() );
|
|
|
|
while( 1 )
|
|
{
|
|
if( m_BufferDescPool.GetInUseCount() == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
Sleep( 20 );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Waiting complete." );
|
|
|
|
return;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::GetSessionDesc"
|
|
//
|
|
// GetSessionDesc
|
|
//
|
|
// Called to retrieve the description of the current session.
|
|
//
|
|
// Called By:
|
|
// - DVS_GetSessionDesc
|
|
//
|
|
// Locks Required:
|
|
// - Global Read Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::GetSessionDesc( LPDVSESSIONDESC lpSessionDesc )
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpSessionDesc = 0x%p", lpSessionDesc );
|
|
|
|
if( lpSessionDesc == NULL || !DNVALID_WRITEPTR(lpSessionDesc,sizeof( DVSESSIONDESC )) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Session desc pointer bad" );
|
|
return DVERR_INVALIDPOINTER;
|
|
}
|
|
|
|
if( lpSessionDesc->dwSize != sizeof( DVSESSIONDESC ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid size on session desc" );
|
|
return DVERR_INVALIDPARAM;
|
|
}
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
guardLock.Lock();
|
|
|
|
// We need to be initialized
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "No host running" );
|
|
return DVERR_NOTHOSTING;
|
|
}
|
|
|
|
memcpy( lpSessionDesc, &m_dvSessionDesc, sizeof( DVSESSIONDESC ) );
|
|
|
|
DV_DUMP_SD( (LPDVSESSIONDESC) lpSessionDesc );
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done, Returning DV_OK" );
|
|
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::SetSessionDesc"
|
|
//
|
|
// SetSessionDesc
|
|
//
|
|
// Used to set session parameters. The only parameters that can be set are the
|
|
// buffer quality and buffer aggresiveness.
|
|
//
|
|
// Called By:
|
|
// - DVS_SetSessionDesc
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::SetSessionDesc(LPDVSESSIONDESC lpSessionDesc )
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVSE::SetSessionDesc() Begin" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpSessionDesc = 0x%p", lpSessionDesc);
|
|
|
|
if( lpSessionDesc == NULL ||
|
|
!DNVALID_READPTR( lpSessionDesc, sizeof(DVSESSIONDESC)) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
|
|
return DVERR_INVALIDPOINTER;
|
|
}
|
|
|
|
DV_DUMP_SD( lpSessionDesc );
|
|
|
|
if( lpSessionDesc->dwSize != sizeof( DVSESSIONDESC ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid size of session desc" );
|
|
return DVERR_INVALIDPARAM;
|
|
}
|
|
|
|
if( !DV_ValidBufferAggresiveness( lpSessionDesc->dwBufferAggressiveness ) ||
|
|
!DV_ValidBufferQuality( lpSessionDesc->dwBufferQuality) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid buffer settings" );
|
|
return DVERR_INVALIDPARAM;
|
|
}
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
|
|
guardLock.Lock();
|
|
|
|
// We need to be initialized
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "No session running" );
|
|
return DVERR_NOTHOSTING;
|
|
}
|
|
|
|
if( m_dvSessionDesc.dwBufferAggressiveness == DVBUFFERAGGRESSIVENESS_DEFAULT )
|
|
{
|
|
m_dvSessionDesc.dwBufferAggressiveness = s_dwDefaultBufferAggressiveness;
|
|
}
|
|
else
|
|
{
|
|
m_dvSessionDesc.dwBufferAggressiveness = lpSessionDesc->dwBufferAggressiveness;
|
|
}
|
|
|
|
if( m_dvSessionDesc.dwBufferQuality == DVBUFFERQUALITY_DEFAULT )
|
|
{
|
|
m_dvSessionDesc.dwBufferQuality = s_dwDefaultBufferQuality;
|
|
}
|
|
else
|
|
{
|
|
m_dvSessionDesc.dwBufferQuality = lpSessionDesc->dwBufferQuality;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::GetCaps"
|
|
//
|
|
// GetCaps
|
|
//
|
|
// Retrieves the caps for the DirectPlayVoiceServer object.
|
|
//
|
|
// Called By:
|
|
// - DVS_GetCaps
|
|
//
|
|
// Locks Required:
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::GetCaps(LPDVCAPS lpdvCaps)
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
|
|
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpdvCaps = 0x%p", lpdvCaps );
|
|
|
|
if( lpdvCaps == NULL ||
|
|
!DNVALID_WRITEPTR(lpdvCaps,sizeof(DVCAPS)) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
|
|
return DVERR_INVALIDPOINTER;
|
|
}
|
|
|
|
DV_DUMP_CAPS( lpdvCaps );
|
|
|
|
if( lpdvCaps->dwSize != sizeof( DVCAPS ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid size" );
|
|
return DVERR_INVALIDPARAM;
|
|
}
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
guardLock.Lock();
|
|
|
|
memcpy( lpdvCaps, &m_dvCaps, sizeof( DVCAPS ) );
|
|
|
|
guardLock.Unlock();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::GetCompressionTypes"
|
|
//
|
|
// GetCompressionTypes
|
|
//
|
|
// This function retrieves a list of the current compression types.
|
|
//
|
|
// Called By:
|
|
// - DVS_GetCompressionTypes
|
|
//
|
|
// Locks Required:
|
|
// NONE
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::GetCompressionTypes( LPVOID lpBuffer, LPDWORD lpdwBufferSize, LPDWORD lpdwNumElements, DWORD dwFlags)
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
|
|
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpBuffer = 0x%p lpdwBufferSize = 0x%p lpdwNumElements = 0x%p dwFlags = 0x%x", lpBuffer, lpdwBufferSize, lpdwNumElements, dwFlags);
|
|
|
|
HRESULT hres = DVCDB_CopyCompressionArrayToBuffer( lpBuffer, lpdwBufferSize, lpdwNumElements, dwFlags );
|
|
|
|
if( hres == DV_OK )
|
|
{
|
|
DV_DUMP_CI( (LPDVCOMPRESSIONINFO) lpBuffer, *lpdwNumElements );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "End" );
|
|
|
|
return hres;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::SetTransmitTarget"
|
|
//
|
|
// SetTransmitTarget
|
|
//
|
|
// This function sets the transmit target for the specified user. Only available in sessions with the
|
|
// DVSESSION_SERVERCONTROLTARGET flag specified.
|
|
//
|
|
// Called By:
|
|
// - DVS_SetTransmitTarget
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::SetTransmitTarget(DVID dvidSource, PDVID pdvidTargets, DWORD dwNumTargets, DWORD dwFlags)
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
|
|
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: dvidSource: 0x%x pdvidTargets: 0x%p dwNumTargets: %d dwFlags: 0x%x", dvidSource, pdvidTargets, dwNumTargets, dwFlags );
|
|
|
|
HRESULT hr;
|
|
|
|
hr = DV_ValidTargetList( pdvidTargets, dwNumTargets );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target list hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
// Flags must be 0.
|
|
if( dwFlags != 0 )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags" );
|
|
return DVERR_INVALIDFLAGS;
|
|
}
|
|
|
|
// We need to be initialized
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not Initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
// We need to be running
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not hosting" );
|
|
return DVERR_NOTCONNECTED;
|
|
}
|
|
|
|
// Only if servercontroltarget is active
|
|
if( !(m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Only available with the DVSESSION_SERVERCONTROLTARGET session flag" );
|
|
return DVERR_NOTALLOWED;
|
|
}
|
|
|
|
// Parameter checks
|
|
if( !m_voiceNameTable.IsEntry( dvidSource ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid source player" );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
if( dwNumTargets > 0 )
|
|
{
|
|
for( DWORD dwIndex = 0; dwIndex < dwNumTargets; dwIndex++ )
|
|
{
|
|
if( !m_voiceNameTable.IsEntry( pdvidTargets[dwIndex] ) )
|
|
{
|
|
if( !m_lpSessionTransport->ConfirmValidGroup( pdvidTargets[dwIndex] ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target player/group" );
|
|
return DVERR_INVALIDTARGET;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( dvidSource == DVID_ALLPLAYERS )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot set the target for all or none" );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
// Grab the player and set the target field
|
|
CDVCSPlayer *pPlayerInfo;
|
|
|
|
hr = m_voiceNameTable.GetEntry( dvidSource, (CVoicePlayer **) &pPlayerInfo, TRUE );
|
|
|
|
if( FAILED( hr ) || pPlayerInfo == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to lookup player entry. hr=0x%x", hr );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
ASSERT_VPLAYER(pPlayerInfo);
|
|
|
|
// We have to ensure that updates to the player's target list do not
|
|
// happen simulatenously between a call to this function and a removal
|
|
// of a player because of a player leaving the session.
|
|
pPlayerInfo->Lock();
|
|
|
|
hr = pPlayerInfo->SetPlayerTargets( pdvidTargets, dwNumTargets );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to set player target hr=0x%x", hr );
|
|
pPlayerInfo->Release();
|
|
return hr;
|
|
}
|
|
|
|
hr = BuildAndSendTargetUpdate( dvidSource, pPlayerInfo );
|
|
|
|
pPlayerInfo->UnLock();
|
|
|
|
pPlayerInfo->Release();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::BuildAndSendTargetUpdate"
|
|
//
|
|
// BuildAndSendTargetUpdate
|
|
//
|
|
// This function builds and sends a message with a target list to the specified
|
|
// user.
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::BuildAndSendTargetUpdate( DVID dvidSource,CVoicePlayer *pPlayerInfo )
|
|
{
|
|
// Grab the player and set the target field
|
|
HRESULT hr;
|
|
PDVTRANSPORT_BUFFERDESC pBufferDesc;
|
|
|
|
// Protect target information
|
|
pPlayerInfo->Lock();
|
|
|
|
DWORD dwTransmitSize;
|
|
PDVPROTOCOLMSG_SETTARGET pSetTargetMsg;
|
|
PVOID pvSendContext;
|
|
|
|
dwTransmitSize = sizeof( DVPROTOCOLMSG_SETTARGET ) + (pPlayerInfo->GetNumTargets()*sizeof(DVID));
|
|
|
|
pBufferDesc = GetTransmitBuffer( dwTransmitSize, &pvSendContext );
|
|
|
|
if( pBufferDesc == NULL )
|
|
{
|
|
pPlayerInfo->UnLock();
|
|
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to alloc memory" );
|
|
return DVERR_BUFFERTOOSMALL;
|
|
}
|
|
|
|
pSetTargetMsg = (PDVPROTOCOLMSG_SETTARGET) pBufferDesc->pBufferData;
|
|
|
|
// Send the message to the player
|
|
pSetTargetMsg->dwType = DVMSGID_SETTARGETS;
|
|
pSetTargetMsg->dwNumTargets = pPlayerInfo->GetNumTargets();
|
|
|
|
memcpy( &pSetTargetMsg[1], pPlayerInfo->GetTargetList(), sizeof( DVID ) * pPlayerInfo->GetNumTargets() );
|
|
|
|
pPlayerInfo->UnLock();
|
|
|
|
hr = m_lpSessionTransport->SendToIDS( &dvidSource, 1, pBufferDesc, pvSendContext, DVTRANSPORT_SEND_GUARANTEED );
|
|
|
|
if( hr == DVERR_PENDING )
|
|
{
|
|
hr = DV_OK;
|
|
}
|
|
else if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to send target set message hr=0x%x", hr );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::GetTransmitTarget"
|
|
//
|
|
// GetTransmitTarget
|
|
//
|
|
// This function returns the current transmission target of the specified user.
|
|
//
|
|
// Only available in sessions with the DVSESSION_SERVERCONTROLTARGET flag.
|
|
//
|
|
// Called By:
|
|
// - DVS_GetTransmitTarget
|
|
//
|
|
// Locks Required:
|
|
// - Global Read Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::GetTransmitTarget(DVID dvidSource, LPDVID lpdvidTargets, PDWORD pdwNumElements, DWORD dwFlags )
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: dvidSource = 0x%x lpdvidTargets = 0x%p pdwNumElements = 0x%p dwFlags = 0x%x", dvidSource, lpdvidTargets, pdwNumElements, dwFlags );
|
|
|
|
if( pdwNumElements == NULL ||
|
|
!DNVALID_WRITEPTR( pdwNumElements, sizeof( DWORD ) ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
|
|
return DVERR_INVALIDPOINTER;
|
|
}
|
|
|
|
if( pdwNumElements != NULL &&
|
|
*pdwNumElements > 0 &&
|
|
!DNVALID_WRITEPTR( lpdvidTargets, (*pdwNumElements)*sizeof(DVID) ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid target list buffer specified" );
|
|
return DVERR_INVALIDPOINTER;
|
|
}
|
|
|
|
if( pdwNumElements == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "You must provider a ptr # of elements" );
|
|
return DVERR_INVALIDPARAM;
|
|
}
|
|
|
|
// Flags must be 0.
|
|
if( dwFlags != 0 )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid flags specified" );
|
|
return DVERR_INVALIDFLAGS;
|
|
}
|
|
|
|
// We need to be initialized
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Object not initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
// We need to be running
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "No session running" );
|
|
return DVERR_NOTCONNECTED;
|
|
}
|
|
|
|
// Only if servercontroltarget is active
|
|
if( !(m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Only available with the DVSESSION_SERVERCONTROLTARGET session flag" );
|
|
return DVERR_NOTALLOWED;
|
|
}
|
|
|
|
if( dvidSource == DVID_ALLPLAYERS )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot get target for all or none" );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
// Parameter checks
|
|
if( !m_voiceNameTable.IsEntry( dvidSource ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Specified player does not exist" );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
// Grab the player and set the target field
|
|
CDVCSPlayer *pPlayerInfo;
|
|
|
|
hr = m_voiceNameTable.GetEntry( dvidSource, (CVoicePlayer **) &pPlayerInfo, TRUE );
|
|
|
|
if( FAILED( hr ) || pPlayerInfo == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to lookup player entry. hr=0x%x", hr );
|
|
return DVERR_INVALIDPLAYER;
|
|
}
|
|
|
|
ASSERT_VPLAYER(pPlayerInfo);
|
|
|
|
pPlayerInfo->Lock();
|
|
|
|
if( *pdwNumElements < pPlayerInfo->GetNumTargets() )
|
|
{
|
|
hr = DVERR_BUFFERTOOSMALL;
|
|
}
|
|
else
|
|
{
|
|
memcpy( lpdvidTargets, pPlayerInfo->GetTargetList(), pPlayerInfo->GetNumTargets()*sizeof(DVID) );
|
|
}
|
|
|
|
*pdwNumElements = pPlayerInfo->GetNumTargets();
|
|
|
|
pPlayerInfo->UnLock();
|
|
|
|
pPlayerInfo->Release();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::Initialize"
|
|
//
|
|
// Initialize
|
|
//
|
|
// This function is responsible for connecting the DirectPlayVoiceServer object
|
|
// to the associated transport session. It sets up the object and makes
|
|
// it ready for a call to StartSession.
|
|
//
|
|
// Called By:
|
|
// - DV_Initialize
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::Initialize( CDirectVoiceTransport *lpTransport, LPDVMESSAGEHANDLER lpdvHandler, LPVOID lpUserContext, LPDWORD lpdwMessages, DWORD dwNumElements )
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Begin" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Param: lpTransport = 0x%p lpdvHandler = 0x%p lpUserContext = 0x%p ", lpTransport, lpdvHandler, lpUserContext );
|
|
|
|
if( lpTransport == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid pointer" );
|
|
return E_POINTER;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "lpdwMessages = 0x%p dwNumElements = %d", lpdwMessages, dwNumElements );
|
|
|
|
hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, TRUE );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid message array hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
|
|
|
|
if( lpdwMessages != NULL )
|
|
{
|
|
for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
|
|
{
|
|
DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
|
|
}
|
|
}
|
|
|
|
CDVCSLock guardLock(&m_csClassLock);
|
|
guardLock.Lock();
|
|
|
|
if( m_dwCurrentState != DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Already Initialized" );
|
|
return DVERR_INITIALIZED;
|
|
}
|
|
|
|
if( lpdvHandler == NULL && lpdwMessages != NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
|
|
return DVERR_NOCALLBACK;
|
|
}
|
|
|
|
SetCurrentState( DVSSTATE_IDLE );
|
|
|
|
BFCSingleLock slLock( &m_csNotifyLock );
|
|
slLock.Lock();
|
|
|
|
m_lpMessageHandler = lpdvHandler;
|
|
|
|
hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
SetCurrentState( DVSSTATE_NOTINITIALIZED );
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "SetNotifyMask Failed hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
m_lpSessionTransport = lpTransport;
|
|
m_lpUserContext = lpUserContext;
|
|
|
|
m_dvidLocal = m_lpSessionTransport->GetLocalID();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "DVSE::Initialize() End" );
|
|
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::ReceiveSpeechMessage"
|
|
//
|
|
// ReceiveSpeechMessage
|
|
//
|
|
// Responsible for processing a speech message when it is received.
|
|
//
|
|
// Called By:
|
|
// - DV_ReceiveSpeechMessage
|
|
//
|
|
// Locks Required:
|
|
// - None
|
|
//
|
|
BOOL CDirectVoiceServerEngine::ReceiveSpeechMessage( DVID dvidSource, LPVOID lpMessage, DWORD dwSize )
|
|
{
|
|
PDVPROTOCOLMSG_FULLMESSAGE lpdvFullMessage;
|
|
|
|
// if we dont' have at least one byte then we are going to bail
|
|
if ( dwSize < 1 )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::ReceiveSpeechMessage() Ignoring zero-byte sized message from=0x%x",
|
|
dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
lpdvFullMessage = (PDVPROTOCOLMSG_FULLMESSAGE) lpMessage;
|
|
|
|
if( !ValidatePacketType( lpdvFullMessage ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::ReceiveSpeechMessage() Ignoring message with invalid packet type, type=0x%x, from=0x%x",
|
|
lpdvFullMessage->dvGeneric.dwType, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
switch( lpdvFullMessage->dvGeneric.dwType )
|
|
{
|
|
case DVMSGID_SPEECHBOUNCE:
|
|
// Ignore speech bounces, only reason we get is because we're sending to all or targetting
|
|
// a client on the same ID as us.
|
|
return TRUE;
|
|
case DVMSGID_CONNECTREQUEST:
|
|
return HandleConnectRequest( dvidSource, static_cast<PDVPROTOCOLMSG_CONNECTREQUEST>(lpMessage), dwSize );
|
|
case DVMSGID_DISCONNECT:
|
|
return HandleDisconnect( dvidSource, static_cast<PDVPROTOCOLMSG_GENERIC>(lpMessage), dwSize);
|
|
case DVMSGID_SETTINGSCONFIRM:
|
|
return HandleSettingsConfirm( dvidSource, static_cast<PDVPROTOCOLMSG_SETTINGSCONFIRM>(lpMessage), dwSize );
|
|
case DVMSGID_SETTINGSREJECT:
|
|
return HandleSettingsReject( dvidSource, static_cast<PDVPROTOCOLMSG_GENERIC>(lpMessage), dwSize );
|
|
case DVMSGID_SPEECHWITHTARGET:
|
|
return HandleSpeechWithTarget( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHWITHTARGET>(lpMessage), dwSize );
|
|
case DVMSGID_SPEECH:
|
|
return HandleSpeech( dvidSource, static_cast<PDVPROTOCOLMSG_SPEECHHEADER>(lpMessage), dwSize );
|
|
default:
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "DVSE::ReceiveSpeechMessage() Ignoring Non-Speech Message id=0x%x from=0x%x",
|
|
lpdvFullMessage->dvGeneric.dwType, dvidSource );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleSpeech"
|
|
// HandleSpeech
|
|
//
|
|
// Handles processing of incoming speech packets (in echo server mode).
|
|
//
|
|
// How speech is handled depends on session type. If the session is client/server, the
|
|
// packet is buffered in the appropriate user's queue. If the session is multicast,
|
|
// the packet is forwarded to the packet's target.
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleSpeech( DVID dvidSource, PDVPROTOCOLMSG_SPEECHHEADER lpdvSpeech, DWORD dwSize )
|
|
{
|
|
HRESULT hr = DV_OK;
|
|
|
|
if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHHEADER ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeech() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - sizeof( DVPROTOCOLMSG_SPEECHHEADER ) ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeech() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO )
|
|
{
|
|
// Bounce speech back to the user
|
|
|
|
PDVTRANSPORT_BUFFERDESC pBufferDesc;
|
|
PVOID pvSendContext;
|
|
PDVPROTOCOLMSG_SPEECHHEADER pvSpeechHeader;
|
|
|
|
pBufferDesc = GetTransmitBuffer( dwSize, &pvSendContext );
|
|
|
|
if( pBufferDesc == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error allocating transmit buffer" );
|
|
return FALSE;
|
|
}
|
|
|
|
pvSpeechHeader = (PDVPROTOCOLMSG_SPEECHHEADER) pBufferDesc->pBufferData;
|
|
memcpy( pvSpeechHeader, lpdvSpeech, dwSize );
|
|
|
|
pvSpeechHeader->dwType = DVMSGID_SPEECHBOUNCE;
|
|
|
|
hr = m_lpSessionTransport->SendToIDS( &dvidSource, 1, pBufferDesc, pvSendContext, 0 );
|
|
|
|
if( hr == DVERR_PENDING )
|
|
{
|
|
hr = DV_OK;
|
|
}
|
|
else if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed sending to ID hr=0x%x", hr );
|
|
}
|
|
|
|
return (hr==DV_OK);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleSpeechWithTarget"
|
|
// HandleSpeechWithTarget
|
|
//
|
|
// Handles processing of incoming speech packets.
|
|
//
|
|
// How speech is handled depends on session type. If the session is client/server, the
|
|
// packet is buffered in the appropriate user's queue. If the session is multicast,
|
|
// the packet is forwarded to the packet's target.
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleSpeechWithTarget( DVID dvidSource, PDVPROTOCOLMSG_SPEECHWITHTARGET lpdvSpeech, DWORD dwSize )
|
|
{
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter");
|
|
|
|
DWORD dwSpeechSize; // Size of speech portion in bytes
|
|
DWORD dwTargetSize; // Size of targetting info in bytes
|
|
PBYTE pSourceSpeech; // Pointer to speech within the packet
|
|
HRESULT hr = DV_OK;
|
|
CVoicePlayer *pTargetPlayer;
|
|
|
|
if ( dwSize < sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) || dwSize < sizeof(DVPROTOCOLMSG_SPEECHWITHTARGET) + lpdvSpeech->dwNumTargets*sizeof(DVID) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeechWithFrom() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
if( lpdvSpeech->dwNumTargets > DV_MAX_TARGETS )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeechWithTarget() Ignoring message with too many targets, targets=0x%x, from=0x%x",
|
|
lpdvSpeech->dwNumTargets, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !ValidateSpeechPacketSize( m_lpdvfCompressionInfo, dwSize - (sizeof(DVPROTOCOLMSG_SPEECHWITHTARGET) + lpdvSpeech->dwNumTargets*sizeof( DVID ) ) ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeechWithFrom() Ignoring message with invalid speech size, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
hr = m_voiceNameTable.GetEntry( dvidSource, &pTargetPlayer, TRUE );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, 1, "Ignoring speech from unknown source hr=0x%x", hr );
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT_VPLAYER(pTargetPlayer);
|
|
|
|
if (!pTargetPlayer->ValidateSpeechPacketForDataRate())
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSpeechWithTarget() "
|
|
"Ignoring message as data exceeded. targets=0x%x from=0x%x",
|
|
lpdvSpeech->dwNumTargets, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
dwTargetSize = lpdvSpeech->dwNumTargets*sizeof(DVID);
|
|
|
|
dwSpeechSize = dwSize - sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) - dwTargetSize;
|
|
|
|
pSourceSpeech = (PBYTE) lpdvSpeech;
|
|
pSourceSpeech += sizeof( DVPROTOCOLMSG_SPEECHWITHTARGET ) + dwTargetSize;
|
|
|
|
// We're a mixing server, queue up the speech.
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
hr = HandleMixingReceive( (CDVCSPlayer *) pTargetPlayer, lpdvSpeech, dwSpeechSize, pSourceSpeech );
|
|
}
|
|
else if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING )
|
|
{
|
|
hr = HandleForwardingReceive( pTargetPlayer, lpdvSpeech, dwSpeechSize, pSourceSpeech );
|
|
}
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, 1, "Unable to receive audio hr=0x%x", hr );
|
|
pTargetPlayer->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Exit");
|
|
|
|
pTargetPlayer->Release();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::DoPlayerDisconnect"
|
|
//
|
|
// Performs the work of removing the specified player from the session.
|
|
//
|
|
// Optionally informs the specified player of their disconnection.
|
|
//
|
|
// Called By:
|
|
// - HandleDisconnect (Player requests disconnect)
|
|
// - RemovePlayer (Dplay tells us player disconnected)
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
void CDirectVoiceServerEngine::DoPlayerDisconnect( DVID dvidPlayer, BOOL bInformPlayer )
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Disconnecting player 0x%x", dvidPlayer );
|
|
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Player disconnect ignored, not running" );
|
|
return;
|
|
}
|
|
|
|
CVoicePlayer *pPlayerInfo = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
// Prevents double simultaneous deletes from crashing server.
|
|
//
|
|
// Retrieve and remove from the nametable the specified player.
|
|
hr = m_voiceNameTable.DeleteAndReturnEntry( dvidPlayer, &pPlayerInfo );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Error retrieving player entry. hr=0x%x Player may have dropped.", hr );
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Or we may have encountered disconnect during host migration" );
|
|
|
|
// If we're peer to peer session, inform players of disconnection
|
|
// Clients will ignore disconnects for players who are not in the session
|
|
//
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
|
|
{
|
|
hr = Send_DeletePlayer( dvidPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending deleteplayer to all hr=0x%x", hr );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sent player Quit to all" );
|
|
}
|
|
|
|
// If specified, send a message to the user to inform them of the disconnection
|
|
// This will prevent client from timing out.
|
|
if( bInformPlayer )
|
|
{
|
|
hr = Send_DisconnectConfirm( dvidPlayer, DV_OK );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending disconnect confirm hr=0x%x", hr );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sent disconnect confirm to player" );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Retrieved record" );
|
|
}
|
|
|
|
ASSERT_VPLAYER(pPlayerInfo);
|
|
|
|
// Mark record as disconnected
|
|
pPlayerInfo->SetDisconnected();
|
|
|
|
// Remove player from the "active" list!
|
|
DNEnterCriticalSection( &m_csPlayerActiveList );
|
|
pPlayerInfo->RemoveFromNotifyList();
|
|
pPlayerInfo->Release();
|
|
DNLeaveCriticalSection( &m_csPlayerActiveList );
|
|
|
|
// If we're peer to peer session, inform players of disconnection
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
|
|
{
|
|
hr = Send_DeletePlayer( dvidPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending deleteplayer to all hr=0x%x", hr );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sent player Quit to all" );
|
|
}
|
|
|
|
// If specified, send a message to the user to inform them of the disconnection
|
|
if( bInformPlayer )
|
|
{
|
|
hr = Send_DisconnectConfirm( dvidPlayer, DV_OK );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending disconnect confirm hr=0x%x", hr );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sent disconnect confirm to player" );
|
|
}
|
|
}
|
|
|
|
DVMSG_DELETEVOICEPLAYER dvDeletePlayer;
|
|
dvDeletePlayer.dvidPlayer = dvidPlayer;
|
|
dvDeletePlayer.dwSize = sizeof( DVMSG_DELETEVOICEPLAYER );
|
|
dvDeletePlayer.pvPlayerContext = pPlayerInfo->GetContext();
|
|
pPlayerInfo->SetContext( NULL );
|
|
|
|
// Release reference for find
|
|
pPlayerInfo->Release();
|
|
|
|
TransmitMessage( DVMSGID_DELETEVOICEPLAYER, &dvDeletePlayer, sizeof( DVMSG_DELETEVOICEPLAYER ) );
|
|
|
|
// Remove this player from everyone's target lists
|
|
FindAndRemoveDeadTarget( dvidPlayer );
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleDisconnect"
|
|
//
|
|
// HandleDisconnect
|
|
//
|
|
// Called when a DVMSGID_DISCONNECT message is received.
|
|
//
|
|
// Called By:
|
|
// - ReceiveSpeechMessage
|
|
//
|
|
// Locks Required:
|
|
// - None
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleDisconnect( DVID dvidSource, PDVPROTOCOLMSG_GENERIC lpdvDisconnect, DWORD dwSize )
|
|
{
|
|
|
|
if ( dwSize != sizeof( DVPROTOCOLMSG_GENERIC ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleDisconnect() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
DoPlayerDisconnect( dvidSource, TRUE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::CreatePlayerEntry"
|
|
//
|
|
// CreatePlayerEntry
|
|
//
|
|
// Performs the actual work of creating a player entity. The work performed depends on the
|
|
// type of session.
|
|
//
|
|
// Called By:
|
|
// - HandleSettingsConfirm
|
|
//
|
|
// Locks Required:
|
|
// - None
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::CreatePlayerEntry( DVID dvidSource, PDVPROTOCOLMSG_SETTINGSCONFIRM lpdvSettingsConfirm, DWORD dwHostOrderID, CVoicePlayer **ppPlayer )
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Requesting create [ID=0x%x]",dvidSource );
|
|
|
|
CVoicePlayer *pNewPlayer;
|
|
|
|
// Complicated.
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
CDVCSPlayer *pNewMixingPlayer = (CDVCSPlayer*)m_fpPlayers.Get();
|
|
ASSERT_VPLAYER(pNewMixingPlayer);
|
|
|
|
hr = pNewMixingPlayer->Initialize( dvidSource, dwHostOrderID,
|
|
lpdvSettingsConfirm->dwFlags, NULL,
|
|
m_dwCompressedFrameSize, m_dwUnCompressedFrameSize,
|
|
&m_fpPlayers, m_dwNumMixingThreads );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing new player record hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
QUEUE_PARAMS queueParams;
|
|
|
|
queueParams.wFrameSize = m_dwCompressedFrameSize;
|
|
queueParams.bInnerQueueSize = m_lpdvfCompressionInfo->wInnerQueueSize;
|
|
queueParams.bMaxHighWaterMark = m_lpdvfCompressionInfo->wMaxHighWaterMark,
|
|
queueParams.iQuality = m_dvSessionDesc.dwBufferQuality;
|
|
queueParams.iHops = 2;
|
|
queueParams.iAggr = m_dvSessionDesc.dwBufferAggressiveness;
|
|
queueParams.bInitHighWaterMark = 2;
|
|
queueParams.wQueueId = dvidSource;
|
|
queueParams.wMSPerFrame = m_lpdvfCompressionInfo->dwTimeout,
|
|
queueParams.pFramePool = m_pFramePool;
|
|
|
|
hr = pNewMixingPlayer->CreateQueue( &queueParams );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create inputqueue hr=0x%x", hr );
|
|
pNewMixingPlayer->Release();
|
|
return hr;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Created mixing player" );
|
|
|
|
pNewPlayer = (CVoicePlayer *) pNewMixingPlayer;
|
|
}
|
|
// Otherwise.. not so complicated
|
|
else
|
|
{
|
|
pNewPlayer = (CVoicePlayer*)m_fpPlayers.Get();
|
|
|
|
if( pNewPlayer == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "CDirectVoiceServerEngine::CreatePlayerEntry() Alloc failure on player struct" );
|
|
return DVERR_OUTOFMEMORY;
|
|
}
|
|
|
|
ASSERT_VPLAYER(pNewPlayer);
|
|
|
|
hr = pNewPlayer->Initialize( dvidSource, dwHostOrderID, lpdvSettingsConfirm->dwFlags, NULL, &m_fpPlayers );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error initializing new player record hr=0x%x", hr );
|
|
return hr;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Created regular player" );
|
|
}
|
|
|
|
hr = m_voiceNameTable.AddEntry( dvidSource, pNewPlayer );
|
|
|
|
// Add failed.. release our entry, destroying player
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Error adding player to nametable hr=0x%x", hr );
|
|
pNewPlayer->Release();
|
|
return hr;
|
|
}
|
|
|
|
// If we're mixing session add this player to the mixing server "add queue"
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
AddPlayerToMixingAddList( pNewPlayer );
|
|
}
|
|
|
|
// Add player to the "active" list!
|
|
DNEnterCriticalSection( &m_csPlayerActiveList );
|
|
pNewPlayer->AddToNotifyList(&m_blPlayerActiveList);
|
|
pNewPlayer->AddRef();
|
|
DNLeaveCriticalSection( &m_csPlayerActiveList );
|
|
|
|
*ppPlayer = pNewPlayer;
|
|
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleSettingsConfirm"
|
|
//
|
|
// HandleSettingsConfirm
|
|
//
|
|
// Called to handle the DVMSGID_SETTINGSCONFIRM message. Creates a player entry for
|
|
// the specified player and optionally informs all players in the session.
|
|
//
|
|
// Called By:
|
|
// - ReceiveSpeechMessage
|
|
//
|
|
// Locks Required:
|
|
// - Global Write Lock
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleSettingsConfirm( DVID dvidSource, PDVPROTOCOLMSG_SETTINGSCONFIRM lpdvSettingsConfirm, DWORD dwSize )
|
|
{
|
|
HRESULT hr;
|
|
CVoicePlayer *pPlayer;
|
|
|
|
if ( dwSize != sizeof( DVPROTOCOLMSG_SETTINGSCONFIRM ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSettingsConfirm() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
if( !ValidateSettingsFlags( lpdvSettingsConfirm->dwFlags ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleSettingsConfirm() Ignoring message with invalid client flags, flags=0x%x, from=0x%x",
|
|
lpdvSettingsConfirm->dwFlags, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "HandleSettingsConfirm: Start [ID=0x%x]", dvidSource );
|
|
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Ignoring settings confirm message, not hosting" );
|
|
return TRUE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Received settings confirm [ID=0x%x]", dvidSource );
|
|
|
|
// See if specified player already exists. If they do then we need to do nothing, not even
|
|
// host migration code below. This prevents duplicate create player messages from causing
|
|
// changes to host migration code.
|
|
if( m_voiceNameTable.IsEntry( dvidSource ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Error creating player entry. [ID=0x%x]", dvidSource );
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Normal during host migration" );
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD dwHostOrderID = 0;
|
|
|
|
// Only run host migration code if host migration is available.
|
|
if( m_fHostMigrationEnabled )
|
|
{
|
|
DNEnterCriticalSection( &m_csHostOrderLock );
|
|
|
|
// This is a host migration version of this message, so re-use existing
|
|
// host order ID
|
|
//
|
|
if( lpdvSettingsConfirm->dwHostOrderID != DVPROTOCOL_HOSTORDER_INVALID )
|
|
{
|
|
dwHostOrderID = lpdvSettingsConfirm->dwHostOrderID;
|
|
|
|
// Further reduce chances of duplicate ID, if we received a host order ID > then
|
|
// the last ID offset the next value by offset again.
|
|
if( dwHostOrderID > m_dwNextHostOrderID )
|
|
{
|
|
m_dwNextHostOrderID += DVMIGRATE_ORDERID_OFFSET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwHostOrderID = m_dwNextHostOrderID;
|
|
m_dwNextHostOrderID++;
|
|
}
|
|
|
|
DNLeaveCriticalSection( &m_csHostOrderLock );
|
|
}
|
|
|
|
hr = CreatePlayerEntry( dvidSource, lpdvSettingsConfirm, dwHostOrderID, &pPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Error creating player entry. [ID=0x%x] hr=0x%x", dvidSource, hr );
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Normal during host migration" );
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Player Created [ID=0x%x]", dvidSource );
|
|
}
|
|
|
|
ASSERT_VPLAYER(pPlayer);
|
|
|
|
if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER )
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sending current player list [ID=0x%x]", dvidSource );
|
|
|
|
hr = SendPlayerList( dvidSource, dwHostOrderID );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unable to send player list to player [ID=0x%x] hr=0x%x", dvidSource, hr );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Playerlist sent [[ID=0x%x]", dvidSource );
|
|
}
|
|
|
|
hr = Send_CreatePlayer( DVID_ALLPLAYERS, pPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Send to all for new player failed hr=0x%x", hr );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Informed players of join of [ID=0x%x]", dvidSource );
|
|
}
|
|
}
|
|
else if( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING )
|
|
{
|
|
hr = Send_CreatePlayer( dvidSource, pPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Send to player for new player failed hr=0x%x", hr );
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Informed player of their own join [ID=0x%x]", dvidSource );
|
|
}
|
|
}
|
|
|
|
|
|
DVMSG_CREATEVOICEPLAYER dvCreatePlayer;
|
|
dvCreatePlayer.dvidPlayer = dvidSource;
|
|
dvCreatePlayer.dwFlags = lpdvSettingsConfirm->dwFlags;
|
|
dvCreatePlayer.dwSize = sizeof( DVMSG_CREATEVOICEPLAYER );
|
|
dvCreatePlayer.pvPlayerContext = NULL;
|
|
|
|
TransmitMessage( DVMSGID_CREATEVOICEPLAYER, &dvCreatePlayer, sizeof( DVMSG_CREATEVOICEPLAYER ) );
|
|
|
|
pPlayer->SetContext( dvCreatePlayer.pvPlayerContext );
|
|
|
|
// Release our reference to the player
|
|
pPlayer->Release();
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done processing [ID=0x%x]", dvidSource );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleSettingsReject"
|
|
//
|
|
// HandleSettingsReject
|
|
//
|
|
// This message type is ignored.
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleSettingsReject( DVID dvidSource, PDVPROTOCOLMSG_GENERIC lpdvGeneric, DWORD dwSize )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::HandleConnectRequest"
|
|
//
|
|
// HandleConnectRequest
|
|
//
|
|
// This function is responsible for responding to user connect requests. It is the server's
|
|
// oportunity to reject or accept players. Called in response to a DVMSGID_CONNECTREQUEST
|
|
// message.
|
|
//
|
|
// Called By:
|
|
// - ReceiveSpeechMessage
|
|
//
|
|
// Locks Required:
|
|
// - Global Read Lock
|
|
//
|
|
BOOL CDirectVoiceServerEngine::HandleConnectRequest( DVID dvidSource, PDVPROTOCOLMSG_CONNECTREQUEST lpdvConnectRequest, DWORD dwSize )
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( dwSize != sizeof( DVPROTOCOLMSG_CONNECTREQUEST ) )
|
|
{
|
|
DPFX( DPFPREP, DVF_ANTIHACK_DEBUG_LEVEL, "DVSE::HandleConnectRequest() Ignoring incorrectly sized message, size=0x%x, from=0x%x",
|
|
dwSize, dvidSource );
|
|
return FALSE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Receive Connect Request.. From [ID=0x%x]", dvidSource );
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Processing Request.. [ID=0x%x]", dvidSource );
|
|
|
|
// Handle case where we've shutdown or starting up and we
|
|
// receive this message
|
|
//
|
|
if( m_dwCurrentState != DVSSTATE_RUNNING )
|
|
{
|
|
hr = Send_ConnectRefuse( dvidSource, DVERR_NOTHOSTING );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Error! Failed on internal send hr=0x%x", hr );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if( !CheckProtocolCompatible( lpdvConnectRequest->ucVersionMajor,
|
|
lpdvConnectRequest->ucVersionMinor,
|
|
lpdvConnectRequest->dwVersionBuild ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Protocol is not compatible. [ID=0x%x] Current=%d.%d b%d User=%d.%d b%d", dvidSource,
|
|
DVPROTOCOL_VERSION_MAJOR,
|
|
DVPROTOCOL_VERSION_MINOR,
|
|
DVPROTOCOL_VERSION_BUILD,
|
|
lpdvConnectRequest->ucVersionMajor,
|
|
lpdvConnectRequest->ucVersionMinor,
|
|
lpdvConnectRequest->dwVersionBuild );
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Rejecting connection request. [ID=0x%x]", dvidSource );
|
|
|
|
hr = Send_ConnectRefuse( dvidSource, DVERR_INCOMPATIBLEVERSION );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Error! Failed on internal send hr=0x%x", hr );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Processing Request..2 [ID=0x%x]", dvidSource );
|
|
|
|
hr = Send_ConnectAccept( dvidSource );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error sending player's connect request: hr=0x%x", hr );
|
|
//// TODO: Handle this case better
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Sent connect request [ID=0x%x]", dvidSource );
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_PLAYERMANAGE_DEBUG_LEVEL, "Processing Request..4 [ID=0x%x]", dvidSource );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::StopTransportSession"
|
|
//
|
|
// StopTransportSession
|
|
//
|
|
// This function is called by the transport when the transport session
|
|
// is stopped.
|
|
//
|
|
// Called By:
|
|
// - DV_NotifyEvent
|
|
//
|
|
// Locks Required:
|
|
// - None
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::StopTransportSession()
|
|
{
|
|
StopSession(0,FALSE,DVERR_SESSIONLOST);
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::StartTransportSession"
|
|
HRESULT CDirectVoiceServerEngine::StartTransportSession( )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::AddPlayer"
|
|
HRESULT CDirectVoiceServerEngine::AddPlayer( DVID dvID )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::RemovePlayer"
|
|
HRESULT CDirectVoiceServerEngine::RemovePlayer( DVID dvID )
|
|
{
|
|
if( m_voiceNameTable.IsEntry( dvID ) )
|
|
{
|
|
DoPlayerDisconnect( dvID, FALSE );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::CreateGroup"
|
|
HRESULT CDirectVoiceServerEngine::CreateGroup( DVID dvID )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::DeleteGroup"
|
|
HRESULT CDirectVoiceServerEngine::DeleteGroup( DVID dvID )
|
|
{
|
|
FindAndRemoveDeadTarget( dvID );
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::AddPlayerToGroup"
|
|
HRESULT CDirectVoiceServerEngine::AddPlayerToGroup( DVID dvidGroup, DVID dvidPlayer )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::RemovePlayerFromGroup"
|
|
HRESULT CDirectVoiceServerEngine::RemovePlayerFromGroup( DVID dvidGroup, DVID dvidPlayer )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::SetCurrentState"
|
|
// SetCurrentState
|
|
//
|
|
// Sets the current state of the client engine
|
|
//
|
|
void CDirectVoiceServerEngine::SetCurrentState( DWORD dwState )
|
|
{
|
|
m_dwCurrentState = dwState;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::MigrateHost"
|
|
//
|
|
// MigrateHost
|
|
//
|
|
// This function is responsible for stoping the host in the case where the host
|
|
// suddenly migrates from this client.
|
|
//
|
|
// In most cases the session will be lost before this occurs on the local object
|
|
// and this will never get called.
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::MigrateHost( DVID dvidNewHost, LPDIRECTPLAYVOICESERVER lpdvServer )
|
|
{
|
|
// Cleanup...
|
|
// return StopSession( DVFLAGS_SYNC, TRUE );
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceClientEngine::InternalSetNotifyMask"
|
|
//
|
|
// SetNotifyMask
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::InternalSetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
|
|
{
|
|
BFCSingleLock slLock( &m_csNotifyLock );
|
|
slLock.Lock();
|
|
|
|
if( m_lpdwMessageElements != NULL )
|
|
{
|
|
delete [] m_lpdwMessageElements;
|
|
}
|
|
|
|
m_dwNumMessageElements = dwNumElements;
|
|
|
|
// Make copies of the message elements into our own message array.
|
|
if( m_dwNumMessageElements > 0 )
|
|
{
|
|
m_lpdwMessageElements = new DWORD[m_dwNumMessageElements];
|
|
|
|
if( m_lpdwMessageElements == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Initialize: Error allocating memory" );
|
|
return DVERR_OUTOFMEMORY;
|
|
}
|
|
|
|
memcpy( m_lpdwMessageElements, lpdwMessages, sizeof(DWORD)*m_dwNumMessageElements );
|
|
}
|
|
else
|
|
{
|
|
m_lpdwMessageElements = NULL;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return DV_OK;
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceClientEngine::SetNotifyMask"
|
|
//
|
|
// SetNotifyMask
|
|
//
|
|
// This function sets the notification mask for the DirectPlayVoice object.
|
|
//
|
|
// The array passed in lpdwMessages specify the ID's of the notifications the user wishes to
|
|
// receive. This or specifying NULL for the array turns on all notifications.
|
|
//
|
|
// Called By:
|
|
// - DVS_SetNotifyMask
|
|
//
|
|
// Locks Required:
|
|
// - m_csNotifyLock (Notification array lock)
|
|
//
|
|
HRESULT CDirectVoiceServerEngine::SetNotifyMask( LPDWORD lpdwMessages, DWORD dwNumElements )
|
|
{
|
|
HRESULT hr;
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Enter" );
|
|
// 7/31/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers, addresses, and handles.
|
|
DPFX(DPFPREP, DVF_APIPARAM, "lpdwMessages = 0x%p dwNumElements = %d", lpdwMessages, dwNumElements );
|
|
|
|
hr = DV_ValidMessageArray( lpdwMessages, dwNumElements, TRUE );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Invalid message array hr=0x%x", hr);
|
|
return hr;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_APIPARAM, "Message IDs=%d", dwNumElements );
|
|
|
|
if( lpdwMessages != NULL )
|
|
{
|
|
for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
|
|
{
|
|
DPFX(DPFPREP, DVF_APIPARAM, "MessageIDs[%d] = %d", dwIndex, lpdwMessages[dwIndex] );
|
|
}
|
|
}
|
|
|
|
if( m_dwCurrentState == DVSSTATE_NOTINITIALIZED )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Not initialized" );
|
|
return DVERR_NOTINITIALIZED;
|
|
}
|
|
|
|
BFCSingleLock slLock( &m_csNotifyLock );
|
|
slLock.Lock();
|
|
|
|
if( m_lpMessageHandler == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Cannot specify message mask there is no callback function" );
|
|
return DVERR_NOCALLBACK;
|
|
}
|
|
|
|
hr = InternalSetNotifyMask( lpdwMessages, dwNumElements );
|
|
|
|
DPFX(DPFPREP, DVF_ENTRYLEVEL, "Done" );
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceClientEngine::CleanupActiveList"
|
|
void CDirectVoiceServerEngine::CleanupActiveList()
|
|
{
|
|
CBilink *pblSearch;
|
|
CVoicePlayer *pVoicePlayer;
|
|
|
|
DNEnterCriticalSection( &m_csPlayerActiveList );
|
|
|
|
pblSearch = m_blPlayerActiveList.GetNext();
|
|
|
|
while( pblSearch != &m_blPlayerActiveList )
|
|
{
|
|
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
|
|
ASSERT_VPLAYER(pVoicePlayer);
|
|
|
|
pblSearch = pblSearch->GetNext();
|
|
|
|
pVoicePlayer->RemoveFromNotifyList();
|
|
pVoicePlayer->Release();
|
|
}
|
|
|
|
DNLeaveCriticalSection( &m_csPlayerActiveList );
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::GetTransmitBuffer"
|
|
PDVTRANSPORT_BUFFERDESC CDirectVoiceServerEngine::GetTransmitBuffer( DWORD dwSize, LPVOID *ppvSendContext )
|
|
{
|
|
PDVTRANSPORT_BUFFERDESC pNewBuffer = NULL;
|
|
DWORD dwFPMIndex = 0xFFFFFFFF;
|
|
DWORD dwWastedSpace = 0xFFFFFFFF;
|
|
DWORD dwSearchFPMIndex;
|
|
|
|
DNEnterCriticalSection( &m_csBufferLock );
|
|
|
|
pNewBuffer = (PDVTRANSPORT_BUFFERDESC) m_BufferDescPool.Get( );
|
|
|
|
DNLeaveCriticalSection( &m_csBufferLock );
|
|
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer desc address 0x%p", (void *) pNewBuffer );
|
|
|
|
if( pNewBuffer == NULL )
|
|
{
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "Error getting transmit buffer" );
|
|
goto GETTRANSMITBUFFER_ERROR;
|
|
}
|
|
|
|
pNewBuffer->lRefCount = 0;
|
|
pNewBuffer->dwObjectType = DVTRANSPORT_OBJECTTYPE_SERVER;
|
|
pNewBuffer->dwFlags = 0;
|
|
pNewBuffer->pBufferData = NULL;
|
|
|
|
for( dwSearchFPMIndex = 0; dwSearchFPMIndex < m_dwNumPools; dwSearchFPMIndex++ )
|
|
{
|
|
// Potential pool
|
|
if( m_pdwBufferPoolSizes[dwSearchFPMIndex] >= dwSize )
|
|
{
|
|
if( m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize < dwWastedSpace )
|
|
{
|
|
dwWastedSpace = m_pdwBufferPoolSizes[dwSearchFPMIndex] - dwSize;
|
|
dwFPMIndex = dwSearchFPMIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( dwFPMIndex == 0xFFFFFFFF )
|
|
{
|
|
DNASSERT( FALSE );
|
|
DPFX(DPFPREP, 0, "Could not find pool large enough for buffer" );
|
|
goto GETTRANSMITBUFFER_ERROR;
|
|
}
|
|
|
|
pNewBuffer->pvContext = &m_pBufferPools[dwFPMIndex];
|
|
|
|
DNEnterCriticalSection( &m_csBufferLock );
|
|
pNewBuffer->pBufferData = (PBYTE) m_pBufferPools[dwFPMIndex].Get();
|
|
DNLeaveCriticalSection( &m_csBufferLock );
|
|
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Got a buffer value at address 0x%p", (void *) pNewBuffer->pBufferData );
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
|
|
|
|
if( pNewBuffer->pBufferData == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Error getting buffer for buffer desc" );
|
|
goto GETTRANSMITBUFFER_ERROR;
|
|
}
|
|
|
|
pNewBuffer->dwBufferSize = dwSize;
|
|
|
|
*ppvSendContext = pNewBuffer;
|
|
|
|
return pNewBuffer;
|
|
|
|
GETTRANSMITBUFFER_ERROR:
|
|
|
|
DNEnterCriticalSection( &m_csBufferLock );
|
|
if( pNewBuffer != NULL && pNewBuffer->pBufferData != NULL )
|
|
{
|
|
((CFixedPool*) pNewBuffer->pvContext)->Release( pNewBuffer->pBufferData );
|
|
}
|
|
|
|
if( pNewBuffer != NULL )
|
|
{
|
|
m_BufferDescPool.Release( pNewBuffer );
|
|
}
|
|
DNLeaveCriticalSection( &m_csBufferLock );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::ReturnTransmitBuffer"
|
|
// ReturnTransmitBuffer
|
|
//
|
|
// PDVTRANSPORT_BUFFERDESC pBufferDesc - Buffer description of buffer to return
|
|
// LPVOID lpvContext - Context value to be used when returning the buffer
|
|
//
|
|
void CDirectVoiceServerEngine::ReturnTransmitBuffer( PVOID pvContext )
|
|
{
|
|
PDVTRANSPORT_BUFFERDESC pBufferDesc = (PDVTRANSPORT_BUFFERDESC) pvContext;
|
|
CFixedPool* pPool = (CFixedPool*) pBufferDesc->pvContext;
|
|
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer desc at address 0x%p", (void *) pBufferDesc );
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: Returning a buffer at address 0x%p", (void *) pBufferDesc->pBufferData );
|
|
|
|
DNEnterCriticalSection( &m_csBufferLock );
|
|
|
|
// Release memory
|
|
pPool->Release( pBufferDesc->pBufferData );
|
|
|
|
// Release buffer description
|
|
m_BufferDescPool.Release( pvContext );
|
|
|
|
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_BufferDescPool.GetInUseCount() );
|
|
|
|
DNLeaveCriticalSection( &m_csBufferLock );
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::SendComplete"
|
|
HRESULT CDirectVoiceServerEngine::SendComplete( PDVEVENTMSG_SENDCOMPLETE pSendComplete )
|
|
{
|
|
ReturnTransmitBuffer( pSendComplete->pvUserContext );
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::SetupBuffers"
|
|
HRESULT CDirectVoiceServerEngine::SetupBuffers()
|
|
{
|
|
HRESULT hr = DV_OK;
|
|
|
|
DWORD dwIndex = 0;
|
|
|
|
m_dwNumPools = SERVER_POOLS_NUM;
|
|
|
|
if (!m_BufferDescPool.Initialize( sizeof(DVTRANSPORT_BUFFERDESC), NULL, NULL, NULL, NULL ))
|
|
{
|
|
DPFX(DPFPREP, 0, "Error allocating memory" );
|
|
hr = DVERR_OUTOFMEMORY;
|
|
goto SETUPBUFFERS_ERROR;
|
|
}
|
|
|
|
m_pBufferPools = new CFixedPool[m_dwNumPools];
|
|
|
|
if( m_pBufferPools == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Error allocating memory" );
|
|
hr = DVERR_OUTOFMEMORY;
|
|
goto SETUPBUFFERS_ERROR;
|
|
}
|
|
|
|
memset( m_pBufferPools, 0x00, sizeof( CFixedPool ) * m_dwNumPools );
|
|
|
|
m_pdwBufferPoolSizes = new DWORD[m_dwNumPools];
|
|
|
|
if( m_pdwBufferPoolSizes == NULL )
|
|
{
|
|
DPFX(DPFPREP, 0, "Error allocating memory" );
|
|
hr = DVERR_OUTOFMEMORY;
|
|
goto SETUPBUFFERS_ERROR;
|
|
}
|
|
|
|
m_pdwBufferPoolSizes[0] = SERVER_POOLS_SIZE_MESSAGE;
|
|
m_pdwBufferPoolSizes[1] = SERVER_POOLS_SIZE_PLAYERLIST;
|
|
m_pdwBufferPoolSizes[2] = sizeof( DVPROTOCOLMSG_SPEECHWITHFROM )+m_dwCompressedFrameSize+COMPRESSION_SLUSH;
|
|
|
|
for( dwIndex = 0; dwIndex < m_dwNumPools; dwIndex++ )
|
|
{
|
|
if(!m_pBufferPools[dwIndex].Initialize( m_pdwBufferPoolSizes[dwIndex], NULL, NULL, NULL, NULL))
|
|
{
|
|
DPFX(DPFPREP, 0, "Error creating transmit buffers" );
|
|
goto SETUPBUFFERS_ERROR;
|
|
}
|
|
}
|
|
|
|
return DV_OK;
|
|
|
|
SETUPBUFFERS_ERROR:
|
|
|
|
FreeBuffers();
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::FreeBuffers"
|
|
HRESULT CDirectVoiceServerEngine::FreeBuffers()
|
|
{
|
|
DWORD dwIndex;
|
|
|
|
if( m_pBufferPools != NULL )
|
|
{
|
|
for( dwIndex = 0; dwIndex < m_dwNumPools; dwIndex++ )
|
|
{
|
|
m_pBufferPools[dwIndex].DeInitialize();
|
|
}
|
|
|
|
delete [] m_pBufferPools;
|
|
|
|
m_pBufferPools = NULL;
|
|
}
|
|
|
|
if( m_pdwBufferPoolSizes != NULL )
|
|
{
|
|
delete [] m_pdwBufferPoolSizes;
|
|
m_pdwBufferPoolSizes = 0;
|
|
}
|
|
|
|
m_BufferDescPool.DeInitialize();
|
|
|
|
m_dwNumPools = 0;
|
|
|
|
return DV_OK;
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::FindAndRemoveDeadTargets"
|
|
//
|
|
// FindAndRemoveDeadTargets
|
|
//
|
|
// This function when called with the server controlled targetting flag active
|
|
// scans the list of players and for each player checks to see if the specified
|
|
// DVID is in their target list. If the player IS in the target list the target
|
|
// list for the player is updated and and update is sent to the client.
|
|
//
|
|
// Parameters:
|
|
// dvidID - ID of the player who has been removed for some reason.
|
|
//
|
|
void CDirectVoiceServerEngine::FindAndRemoveDeadTarget( DVID dvidID )
|
|
{
|
|
if( !(m_dvSessionDesc.dwFlags & DVSESSION_SERVERCONTROLTARGET) )
|
|
return;
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Player/Group ID [0x%x] was removed, checking player target lists", dvidID );
|
|
|
|
// Grab the active player list lock so we can run the list
|
|
DNEnterCriticalSection( &m_csPlayerActiveList );
|
|
|
|
CBilink *pblSearch = NULL;
|
|
CVoicePlayer *pPlayer = NULL;
|
|
HRESULT hr = DV_OK;
|
|
|
|
pblSearch = m_blPlayerActiveList.GetNext();
|
|
|
|
while( pblSearch != &m_blPlayerActiveList )
|
|
{
|
|
pPlayer = CONTAINING_RECORD(pblSearch, CVoicePlayer, m_blNotifyList);
|
|
ASSERT_VPLAYER(pPlayer);
|
|
|
|
// Lock the specified player -- we have to to ensure that we don't get another
|
|
// simultaneous taregtting update that races this one.
|
|
pPlayer->Lock();
|
|
|
|
// Specified target was in this player's target list
|
|
if( pPlayer->FindAndRemovePlayerTarget(dvidID) )
|
|
{
|
|
// Send an update to the client
|
|
hr = BuildAndSendTargetUpdate( pPlayer->GetPlayerID(),pPlayer );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Unable to send target update to player [0x%x] hr=[0x%x]", pPlayer->GetPlayerID(), hr );
|
|
}
|
|
}
|
|
|
|
pPlayer->UnLock();
|
|
|
|
pblSearch = pblSearch->GetNext();
|
|
}
|
|
|
|
DNLeaveCriticalSection( &m_csPlayerActiveList );
|
|
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::ValidateSettingsFlags"
|
|
BOOL CDirectVoiceServerEngine::ValidateSettingsFlags( DWORD dwFlags )
|
|
{
|
|
return ((dwFlags == 0) || (dwFlags == DVPLAYERCAPS_HALFDUPLEX));
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::ValidatePacketType"
|
|
BOOL CDirectVoiceServerEngine::ValidatePacketType( const DVPROTOCOLMSG_FULLMESSAGE* lpdvFullMessage ) const
|
|
{
|
|
|
|
switch( lpdvFullMessage->dvGeneric.dwType )
|
|
{
|
|
case DVMSGID_SPEECHWITHTARGET:
|
|
return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_FORWARDING ) ||
|
|
( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ) );
|
|
break;
|
|
case DVMSGID_SPEECH:
|
|
return ( ( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_MIXING ) ||
|
|
( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_PEER ) ||
|
|
( m_dvSessionDesc.dwSessionType == DVSESSIONTYPE_ECHO ) );
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CDirectVoiceServerEngine::IsHostMigrationAllowed"
|
|
//
|
|
// This function checks to see if host migration is enabled. Host migration is available
|
|
// if we're in peer-to-peer mode.
|
|
//
|
|
BOOL CDirectVoiceServerEngine::IsHostMigrationEnabled( ) const
|
|
{
|
|
// If it's disabled, it's off.
|
|
if( m_dvSessionDesc.dwFlags & DVSESSION_NOHOSTMIGRATION )
|
|
return FALSE;
|
|
|
|
// Only migrate in peer-to-peer
|
|
if( m_dvSessionDesc.dwSessionType != DVSESSIONTYPE_PEER )
|
|
return FALSE;
|
|
|
|
// Only migrate in peer-to-peer transport
|
|
if( m_dwTransportSessionType != DVTRANSPORT_SESSION_PEERTOPEER )
|
|
return FALSE;
|
|
|
|
// No host migration allowed on transport, so none on our session.
|
|
if( !(m_dwTransportFlags & DVTRANSPORT_MIGRATEHOST) )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|