|
|
/*==========================================================================
* * 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; }
|