/*========================================================================== * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: dvdxtran.cpp * Content: Implementation of transport class providing DirectXVoice transport * through the IDirectXVoiceTransport interface. * * History: * Date By Reason * ==== == ====== * 07/23/99 rodtoll Modified from dvdptran.cpp * 08/03/99 rodtoll Modified to conform to new base class and minor fixes * for dplay integration. * 08/04/99 rodtoll Modified to allow group targets * 08/10/99 rodtoll Removed TODO pragmas * 08/25/99 rodtoll Fixed group membership check * 08/30/99 rodtoll Modified SendToServer to send to the server player in * client/server sessions. * 08/31/99 rodtoll Updated to use new debug libs * 09/01/99 rodtoll Updated so that constructor no longer calls into dplay * rodtoll Added check for valid pointers in func calls * 09/02/99 rodtoll Added checks to handle case no local player created * 09/20/99 rodtoll Added memory alloc failure checks * 09/21/99 rodtoll Fixed memory leak * 10/05/99 rodtoll Additional comments and DPF_MODNAMEs * 11/23/99 rodtoll Split CheckForValid into Group and Player * 12/16/99 rodtoll Bug #122629 - As part of new host migration update how * sends to server before first response are sent. * 01/14/2000 rodtoll Renamed SendToID to SendToIDS and updated parameter list * to accept multiple targets. * rodtoll Added GetNumPlayers call * 01/17/2000 rodtoll Debug statement removed that limited max players to 30 * 03/28/2000 rodtoll Moved nametable from here to upper level classes * rodtoll Removed uneeded functions/members * 04/07/2000 rodtoll Updated to support new DP <--> DPV interface * rodtoll Updated to support no copy sends * rodtoll Bug #32179 - Prevent multiple client/server registrations on transport * 06/21/2000 rodtoll Bug #36820 - Host migrates to wrong client when client/server are on same interface * Condition exists where host sends leave message, client attempts to start new host * which fails because old host still registered. Now deregistering is two step * process DisableReceiveHook then DestroyTransport. * 07/22/20000 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. * 01/04/2001 rodtoll WinBug #94200 - Remove stray comments * 01/22/2001 rodtoll WINBUG #288437 - IA64 Pointer misalignment due to wire packets * ***************************************************************************/ #include "dxvoicepch.h" #define DVF_SEND_DEBUG_LEVEL DVF_INFOLEVEL #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::CDirectVoiceDirectXTransport" CDirectVoiceDirectXTransport::CDirectVoiceDirectXTransport( LPDIRECTPLAYVOICETRANSPORT lpTransport ): m_lpTransport(NULL), m_dpidServer(DPID_ALLPLAYERS), m_dpidLocalPlayer(0), m_bLocalServer(TRUE), m_bActiveSession(TRUE), m_dwTransportFlags(0), m_lpVoiceEngine(NULL), m_dwMaxPlayers(0), m_initialized(FALSE), m_dwObjectType(0), m_fAdvised(FALSE) { lpTransport->QueryInterface( IID_IDirectPlayVoiceTransport, (void **) &m_lpTransport ); m_dvTransportInfo.dwSize = sizeof( DVTRANSPORTINFO ); } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::~CDirectVoiceDirectXTransport" CDirectVoiceDirectXTransport::~CDirectVoiceDirectXTransport() { if( m_lpTransport != NULL ) m_lpTransport->Release(); } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::DestroyTransport" // DestroyTransport // // This method is used to remove last references to transport that transport // layer has. There was a memory leak where // // void CDirectVoiceDirectXTransport::DestroyTransport() { if( m_lpTransport != NULL ) { m_lpTransport->Release(); m_lpTransport = NULL; } } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::Initialize" // // Initialize // // Called from the transport when Advise is called. // // Used to initialize this object. // HRESULT CDirectVoiceDirectXTransport::Initialize( ) { HRESULT hr; hr = m_lpTransport->GetSessionInfo( &m_dvTransportInfo ); if( FAILED( hr ) ) { DPFX(DPFPREP, 0, "DXVT::Initialize: GetSessionInfo() failed! hr=0x%x", hr ); return hr; } m_dwMaxPlayers = (m_dvTransportInfo.dwMaxPlayers==0) ? 255 : m_dvTransportInfo.dwMaxPlayers; m_dpidLocalPlayer = m_dvTransportInfo.dvidLocalID; // No longer needed, the server may not bee the host of the dplay session // m_dpidServer = m_dvTransportInfo.dvidSessionHost; m_dpidServer = DPID_ALLPLAYERS; m_initialized = TRUE; return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::GetMaxPlayers" DWORD CDirectVoiceDirectXTransport::GetMaxPlayers( ) { return m_dwMaxPlayers; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::SendHelper" HRESULT CDirectVoiceDirectXTransport::SendHelper( UNALIGNED DVID * pdvidTargets, DWORD dwNumTargets, PDVTRANSPORT_BUFFERDESC pBufferDesc, LPVOID pvContext, DWORD dwFlags ) { HRESULT hr; if( dwNumTargets > 1 ) { DNASSERT(pdvidTargets); DEBUG_ONLY( for( DWORD dwIndex = 0; dwIndex < dwNumTargets; dwIndex++ ) { ) DPFX(DPFPREP, DVF_SEND_DEBUG_LEVEL, "Using multitargetted send [From=0x%x To=0x%x]", m_dpidLocalPlayer, pdvidTargets[dwIndex] ); DEBUG_ONLY( } ) hr = m_lpTransport->SendSpeechEx( m_dpidLocalPlayer, dwNumTargets, pdvidTargets, pBufferDesc, pvContext, dwFlags ); } else if (dwNumTargets==1) { DNASSERT(pdvidTargets); DPFX(DPFPREP, DVF_SEND_DEBUG_LEVEL, "Single target for send [From=0x%x To=0x%x]", m_dpidLocalPlayer, pdvidTargets[0] ); hr = m_lpTransport->SendSpeech( m_dpidLocalPlayer, *pdvidTargets, pBufferDesc, pvContext, dwFlags ); } else { //no targets specified, so simply return the send buffer and do nothing DVEVENTMSG_SENDCOMPLETE sendCompleteEvent; sendCompleteEvent.pvUserContext=pvContext; sendCompleteEvent.hrSendResult=DPN_OK; m_lpVoiceEngine->SendComplete(&sendCompleteEvent); DPFX(DPFPREP, DVF_SEND_DEBUG_LEVEL, "Ignoring send from 0x%x, as dwNumTargets==0", m_dpidLocalPlayer); hr=DPN_OK; } /* DNASSERT( pdpidTargets != NULL ); for( DWORD dwIndex = 0; dwIndex < dwNumTargets; dwIndex++ ) { hr = m_lpTransport->SendSpeech( m_dpidLocalPlayer, pdpidTargets[dwIndex], lpBuffer, dwSize, dwFlags ); } */ return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::SendToServer" HRESULT CDirectVoiceDirectXTransport::SendToServer( PDVTRANSPORT_BUFFERDESC pBufferDesc, LPVOID pvContext, DWORD dwFlags ) { if( m_dvTransportInfo.dwSessionType == DVTRANSPORT_SESSION_CLIENTSERVER ) { DVID dvidTmp = DVID_SERVERPLAYER; DPFX(DPFPREP, DVF_SEND_DEBUG_LEVEL, "Sending to standard server player" ); return SendHelper( &dvidTmp, 1, pBufferDesc, pvContext, dwFlags ); } else { DPFX(DPFPREP, DVF_SEND_DEBUG_LEVEL, "Sending to server ID [ID=0x%x]", m_dpidServer ); return SendHelper( &m_dpidServer, 1, pBufferDesc, pvContext, dwFlags ); } } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::SendToIDS" HRESULT CDirectVoiceDirectXTransport::SendToIDS( UNALIGNED DVID * pdvidTargets, DWORD dwNumTargets, PDVTRANSPORT_BUFFERDESC pBufferDesc, LPVOID pvContext, DWORD dwFlags ) { return SendHelper( pdvidTargets, dwNumTargets, pBufferDesc, pvContext, dwFlags ); } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::SendToAll" HRESULT CDirectVoiceDirectXTransport::SendToAll( PDVTRANSPORT_BUFFERDESC pBufferDesc, LPVOID pvContext, DWORD dwFlags ) { DVID dvidTmp = DPID_ALLPLAYERS; return SendHelper( &dvidTmp, 1, pBufferDesc, pvContext, dwFlags ); } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::ConfirmValidGroup" BOOL CDirectVoiceDirectXTransport::ConfirmValidGroup( DVID dvid ) { if( dvid == DVID_ALLPLAYERS ) { return TRUE; } else if( m_dvTransportInfo.dwSessionType == DVTRANSPORT_SESSION_CLIENTSERVER ) { return TRUE; } else { BOOL fResult; HRESULT hr; hr = m_lpTransport->IsValidGroup( dvid, &fResult ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Error confirming valid group hr=0x%x", hr ); return FALSE; } else { return fResult; } } return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::ConfirmValidEntity" // // ConfirmValidEntity // // Checks to ensure that the ID passed is a valid one for the session. // // Will return TRUE if the player iD is one of: // DVID_ALLPLAYERS, DVID_NOTARGET, (any value in client/server mode), // a player in the map, or a valid Transport group. // BOOL CDirectVoiceDirectXTransport::ConfirmValidEntity( DVID dvid ) { if( m_dvTransportInfo.dwSessionType == DVTRANSPORT_SESSION_CLIENTSERVER ) { return TRUE; } else { BOOL fResult; m_lpTransport->IsValidEntity( dvid, &fResult ); return fResult; } return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::EnableReceiveHook" // // EnableReceiveHook // // This is used to activate the connection between the transport // and the transport class. // // We call advise, which will cause Initialize to be called on this // class before we return from Advise. // HRESULT CDirectVoiceDirectXTransport::EnableReceiveHook( LPDIRECTVOICEOBJECT dvObject, DWORD dwObjectType ) { HRESULT hr; m_lpVoiceEngine = dvObject->lpDVEngine; m_dwObjectType = dwObjectType; // The transport will call Initialize on our notification interface // before returning from this function. // // Once we've returned from this function we should be ok. // FYI: This is safe, but using QueryInterface would be more "correct". However, we would // need to determine if this is a client or a server and call the appropriate QueryInterface. hr = m_lpTransport->Advise( (LPUNKNOWN) dvObject, m_dwObjectType ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Advise failed. hr=0x%x", hr ); m_fAdvised = FALSE; } else { m_fAdvised = TRUE; } return hr; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::DisableReceiveHook" HRESULT CDirectVoiceDirectXTransport::WaitForDetachCompletion() { DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "# of threads remaining: %d", m_lRefCount ); // Loop until all threads are done inside our layer while( m_lRefCount > 0 ) Sleep( 5 ); return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::DisableReceiveHook" // // DisableReceiveHook // // Removes the hooks into the transport and releases the interface // reference this object holds for the transport. // // Also responsible for destroying the list of players maintained // by this object. // HRESULT CDirectVoiceDirectXTransport::DisableReceiveHook( ) { if( m_fAdvised ) { m_lpTransport->UnAdvise( m_dwObjectType ); m_fAdvised = FALSE; m_initialized = FALSE; DPFX(DPFPREP, DVF_DISCONNECT_PROCEDURE_DEBUG_LEVEL, "Unhooking Transport" ); } // When this is done no more indications will be waiting. return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::ConfirmLocalHost" BOOL CDirectVoiceDirectXTransport::ConfirmLocalHost( ) { if( !m_initialized ) m_lpTransport->GetSessionInfo( &m_dvTransportInfo ); if( m_dvTransportInfo.dwFlags & DVTRANSPORT_LOCALHOST ) return TRUE; else return FALSE; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::ConfirmSessionActive" BOOL CDirectVoiceDirectXTransport::ConfirmSessionActive( ) { return TRUE; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::GetTransportSettings" HRESULT CDirectVoiceDirectXTransport::GetTransportSettings( LPDWORD lpdwSessionType, LPDWORD lpdwFlags ) { HRESULT hr = DV_OK; if( !m_initialized ) hr = m_lpTransport->GetSessionInfo( &m_dvTransportInfo ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to retrieve transport settings" ); return hr; } *lpdwSessionType = m_dvTransportInfo.dwSessionType; *lpdwFlags = m_dvTransportInfo.dwFlags; return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::AddPlayerEntry" HRESULT CDirectVoiceDirectXTransport::AddPlayerEntry( DVID dvidPlayer, LPVOID lpData ) { return DVERR_NOTSUPPORTED; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::DeletePlayerEntry" HRESULT CDirectVoiceDirectXTransport::DeletePlayerEntry( DVID dvidPlayer ) { return DVERR_NOTSUPPORTED; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::GetPlayerEntry" // // GetPlayerEntry // // Retrieves the player record for the specified player (if it exists). // HRESULT CDirectVoiceDirectXTransport::GetPlayerEntry( DVID dvidPlayer, CVoicePlayer **lplpPlayer ) { return DVERR_NOTSUPPORTED; } //////////////////////////////////////////////////////////////////////// // // USEFUL FOR REMOTE VOICE SESSIONS // #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::CreateGroup" HRESULT CDirectVoiceDirectXTransport::CreateGroup( LPDVID dvidGroup ) { return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::DeleteGroup" HRESULT CDirectVoiceDirectXTransport::DeleteGroup( DVID dvidGroup ) { return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::AddPlayerToGroup" HRESULT CDirectVoiceDirectXTransport::AddPlayerToGroup( LPDVID dvidGroup, DVID dvidPlayer ) { return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::RemovePlayerFromGroup" HRESULT CDirectVoiceDirectXTransport::RemovePlayerFromGroup( DVID dvidGroup, DVID dvidPlayer ) { return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::IsPlayerInGroup" BOOL CDirectVoiceDirectXTransport::IsPlayerInGroup( DVID dvidGroup, DVID dvidPlayer ) { if( dvidGroup == DVID_ALLPLAYERS ) { return TRUE; } if( dvidGroup == dvidPlayer ) { return TRUE; } return (m_lpTransport->IsGroupMember( dvidGroup, dvidPlayer )==DV_OK); } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::MigrateHost" // // MigrateHost // // Updates server DPID to match new host // HRESULT CDirectVoiceDirectXTransport::MigrateHost( DVID dvidNewHost ) { DPFX(DPFPREP, DVF_HOSTMIGRATE_DEBUG_LEVEL, "HOST MIGRATION: Setting host to 0x%x", dvidNewHost ); m_dpidServer = dvidNewHost; return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "CDirectVoiceDirectXTransport::MigrateHost" DVID CDirectVoiceDirectXTransport::GetLocalID() { m_dwDuumy = m_dpidLocalPlayer; return m_dpidLocalPlayer; }