Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2761 lines
81 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
*
***************************************************************************/
#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_pBufferDescPool(NULL),
m_pStats(NULL),
m_fCritSecInited(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_timer = 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 ) );
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;
ZeroMemory( &m_perfAppInfo, sizeof( PERF_APPLICATION_INFO ) );
m_perfInfo.dwFlags = PERF_APPLICATION_VALID | PERF_APPLICATION_VOICE | PERF_APPLICATION_SERVER;
CoCreateGuid( &m_perfInfo.guidApplicationInstance );
CoCreateGuid( &m_perfInfo.guidInternalInstance );
m_perfInfo.guidIID = IID_IDirectPlayVoiceServer;
m_perfInfo.dwProcessID = GetCurrentProcessId();
m_perfInfo.dwMemoryBlockSize = sizeof( MixingServerStats ) + sizeof( ServerStats );
// Initilalize the new performance library
hr = PERF_AddEntry( &m_perfInfo, &m_perfAppInfo );
// This is an error, but not fatal
if( FAILED ( hr ) )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to create performance tracking object 0x%x", hr );
}
if( m_perfAppInfo.pbMemoryBlock )
{
m_pStats = (MixingServerStats *) (m_perfAppInfo.pbMemoryBlock+sizeof( ServerStats ));
m_pServerStats = (ServerStats *) m_perfAppInfo.pbMemoryBlock;
}
else
{
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_fpCSPlayers.Initialize();
}
else
{
fPoolsInit = m_fpPlayers.Initialize();
}
InitBilink( &m_blPlayerActiveList, NULL );
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:
PERF_RemoveEntry( m_perfInfo.guidInternalInstance, &m_perfAppInfo );
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 )
{
if( fPoolsInit )
{
m_fpCSPlayers.Deinitialize();
}
delete m_pFramePool;
m_pFramePool = NULL;
}
else
{
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;
}
// 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" );
// Kill the FPM
m_fpCSPlayers.Deinitialize();
delete m_pFramePool;
m_pFramePool = NULL;
}
else
{
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Cleanup.." );
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" );
DPFX(DPFPREP, DVF_INFOLEVEL, ">> Removing perf info" );
PERF_RemoveEntry( m_perfInfo.guidInternalInstance, &m_perfAppInfo );
DPFX(DPFPREP, DVF_INFOLEVEL, "<< Removing perf info" );
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_pBufferDescPool->nInUse );
while( 1 )
{
DNEnterCriticalSection( &m_pBufferDescPool->cs );
if( m_pBufferDescPool->nInUse == 0 )
{
DNLeaveCriticalSection( &m_pBufferDescPool->cs );
break;
}
DNLeaveCriticalSection( &m_pBufferDescPool->cs );
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;
}
// 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;
}
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;
}
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;
hr = m_voiceNameTable.GetEntry( dvidPlayer, &pPlayerInfo, TRUE );
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" );
}
// Mark record as disconnected
pPlayerInfo->SetDisconnected();
// Release reference and remove from the nametable
m_voiceNameTable.DeleteEntry( dvidPlayer );
// 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 = m_fpCSPlayers.Get();
hr = pNewMixingPlayer->Initialize( dvidSource, dwHostOrderID,
lpdvSettingsConfirm->dwFlags, NULL,
m_dwCompressedFrameSize, m_dwUnCompressedFrameSize,
&m_fpCSPlayers, 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 = m_fpPlayers.Get();
if( pNewPlayer == NULL )
{
DPFX(DPFPREP, DVF_ERRORLEVEL, "CDirectVoiceServerEngine::CreatePlayerEntry() Alloc failure on player struct" );
return DVERR_OUTOFMEMORY;
}
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 );
DWORD dwHostOrderID;
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 );
}
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()
{
BILINK *pblSearch;
CVoicePlayer *pVoicePlayer;
DNEnterCriticalSection( &m_csPlayerActiveList );
pblSearch = m_blPlayerActiveList.next;
while( pblSearch != &m_blPlayerActiveList )
{
pVoicePlayer = CONTAINING_RECORD( pblSearch, CVoicePlayer, m_blNotifyList );
pblSearch = pblSearch->next;
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_pBufferDescPool->Get( m_pBufferDescPool );
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(m_pBufferPools[dwFPMIndex]);
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_pBufferDescPool->nInUse );
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 )
{
((PFPOOL) pNewBuffer->pvContext)->Release( ((PFPOOL) pNewBuffer->pvContext), pNewBuffer->pBufferData );
}
if( pNewBuffer != NULL )
{
m_pBufferDescPool->Release( m_pBufferDescPool, 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;
PFPOOL pPool = (PFPOOL) 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( pPool, pBufferDesc->pBufferData );
// Release buffer description
m_pBufferDescPool->Release( m_pBufferDescPool, pvContext );
DPFX(DPFPREP, DVF_BUFFERDESC_DEBUG_LEVEL, "BUFFERDESC: nInUse = %i", m_pBufferDescPool->nInUse );
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;
m_pBufferDescPool = FPM_Create( sizeof(DVTRANSPORT_BUFFERDESC), NULL, NULL, NULL, NULL,
&m_pServerStats->m_dwBufferDescOustanding,
&m_pServerStats->m_dwBufferDescAllocated );
if( m_pBufferDescPool == NULL )
{
DPFX(DPFPREP, 0, "Error allocating memory" );
hr = DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
m_pBufferPools = new PFPOOL[m_dwNumPools];
if( m_pBufferPools == NULL )
{
DPFX(DPFPREP, 0, "Error allocating memory" );
hr = DVERR_OUTOFMEMORY;
goto SETUPBUFFERS_ERROR;
}
memset( m_pBufferPools, 0x00, sizeof( PFPOOL ) * 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++ )
{
m_pBufferPools[dwIndex] = FPM_Create( m_pdwBufferPoolSizes[dwIndex], NULL, NULL, NULL, NULL,
&m_pServerStats->m_dwPacketsOutstanding[dwIndex],
&m_pServerStats->m_dwPacketsAllocated[dwIndex] );
if( m_pBufferPools == 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++ )
{
if( m_pBufferPools[dwIndex] != NULL )
m_pBufferPools[dwIndex]->Fini(m_pBufferPools[dwIndex], FALSE);
}
delete [] m_pBufferPools;
m_pBufferPools = NULL;
}
if( m_pdwBufferPoolSizes != NULL )
{
delete [] m_pdwBufferPoolSizes;
m_pdwBufferPoolSizes = 0;
}
if( m_pBufferDescPool != NULL )
{
m_pBufferDescPool->Fini( m_pBufferDescPool, FALSE );
m_pBufferDescPool = NULL;
}
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 );
BILINK *pblSearch = NULL;
CVoicePlayer *pPlayer = NULL;
HRESULT hr = DV_OK;
pblSearch = m_blPlayerActiveList.next;
while( pblSearch != &m_blPlayerActiveList )
{
pPlayer = CONTAINING_RECORD(pblSearch, CVoicePlayer, m_blNotifyList);
DNASSERT( 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->next;
}
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( PDVPROTOCOLMSG_FULLMESSAGE lpdvFullMessage )
{
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;
}