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