Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3223 lines
88 KiB

/*==========================================================================
*
* Copyright (C) 1999-2002 Microsoft Corporation. All Rights Reserved.
*
* File: Connect.cpp
* Content: DNET connection routines
*@@BEGIN_MSINTERNAL
* History:
* Date By Reason
* ==== == ======
* 09/01/99 mjn Created
* 12/23/99 mjn Hand all NameTable update sends from Host to worker thread
* 12/23/99 mjn Fixed Host and AllPlayers short-cut pointer use
* 12/28/99 mjn Moved Async Op stuff to Async.h
* 12/29/99 mjn Reformed DN_ASYNC_OP to use hParentOp instead of lpvUserContext
* 12/29/99 mjn Turned off Instance GUID verification - TODO - turn back on !
* 01/06/00 mjn Moved NameTable stuff to NameTable.h
* 01/07/00 mjn Moved Misc Functions to DNMisc.h
* 01/07/00 mjn DNHostVerifyConnect ensures connection to Host player only
* 01/08/00 mjn Failed connection returns HRESULT and buffer from Host
* 01/08/00 mjn Added group owner to NameTable
* 01/08/00 mjn Changed DNERR_INVALIDHOST to DNERR_NOTHOST
* 01/08/00 mjn Removed unused connection info
* 01/09/00 mjn Transfer Application Description at connect
* 01/10/00 mjn Fixed Application Description usage
* 01/11/00 mjn Use CPackedBuffers instead of DN_ENUM_BUFFER_INFOs
* 01/13/00 mjn Removed DIRECTNETOBJECT from Pack/UnpackApplicationDesc
* 01/14/00 mjn Removed pvUserContext from DN_NAMETABLE_ENTRY
* 01/14/00 mjn Added password to DN_APPLICATION_DESC_PACKED_INFO
* 01/15/00 mjn Replaced DN_COUNT_BUFFER with CRefCountBuffer
* 01/16/00 mjn Moved User callback stuff to User.h
* 01/17/00 mjn Fixed ConnectToPeer function names
* 01/18/00 mjn Moved Pack/UnpackNameTableInfo to NameTable.cpp
* 01/24/00 mjn Replaced on-wire message pointers to offsets
* 02/01/00 mjn Implement Player/Group context values
* 03/23/00 mjn Set player context through Connect
* 03/24/00 mjn Set player context through INDICATE_CONNECT notification
* 04/03/00 mjn Verify DNET version on connect
* 04/09/00 mjn Modified Connect process to use CAsyncOp
* 04/16/00 mjn DNSendMessage() uses CAsyncOp
* 04/18/00 mjn CConnection tracks connection status better
* mjn Fixed player count problem
* mjn Return connect user reply buffer
* 04/19/00 mjn Fixed DNConnectToHost2 to set DirectNet object flags to CONNECTED
* mjn Update NameTable operations to use DN_WORKER_JOB_SEND_NAMETABLE_OPERATION
* 04/20/00 mjn Host queries for connecting players' address if not specified
* 05/03/00 mjn Prevent unrequired RETURN_BUFFER message when CONNECT fails on Host player
* 05/03/00 mjn Use GetHostPlayerRef() rather than GetHostPlayer()
* 05/08/00 mjn Host sets connecting player's Connection as soon as player entry created
* 05/09/00 mjn Fixed New Player connection sequence to send NAMETABLE_ACK earlier
* 05/23/00 mjn Added DNConnectToPeerFailed()
* 06/14/00 mjn Added DNGetLocalAddress()
* 06/19/00 mjn Fixed connect process to better handle ALL_ADAPTERS case
* 06/22/00 mjn NameTable::UnpackNameTableInfo() returns local players DPNID
* mjn Replace CConnection::MakeConnecting(),MakeConnected() with SetStatus()
* 06/24/00 mjn Added DNHostDropPlayer() to handle failed existing player connects to new players
* 06/25/00 mjn Added code to update lobby when DISCONNECTED
* 06/26/00 mjn Indicate COULDNOTCONNECT to lobby if connect to Host fails
* 06/27/00 rmt Added abstraction for COM_Co(Un)Initialize
* mjn Host will attempt to determine connecting player's address if not specified in connect block
* 07/05/00 mjn More robust handling of disconnecting joining players during connection process
* 07/06/00 mjn More connect fixes
* 07/20/00 mjn The CONNECT process was revamped - new completions, asyncop structure, messages
* mjn Better error handling for shutdown in DNHostVerifyConnect()
* mjn Fixed up DNHostDropPlayer() to inform NewPlayer of drop
* 07/21/00 mjn Added code to handle unreachable players during connect process
* 07/22/00 mjn Extract connecting player's DNET version
* 07/25/00 mjn Update connect parent async op's result at end of DNConnectToHost2()
* 07/27/00 mjn Fixed locking problem with CAsyncOp::MakeChild()
* 07/29/00 mjn Save connection in DNConnectToHost1() on connect parent for better clean up
* 07/30/00 mjn Renamed DNGetLocalAddress() to DNGetLocalDeviceAddress()
* 07/31/00 mjn Added hrReason to DNTerminateSession()
* mjn Added dwDestroyReason to DNHostDisconnect()
* 08/02/00 mjn Removed unused code
* 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
* 08/05/00 rmt Bug #41356 - DPLAY8: CORE: Connections refused by DPlay leak address objects
* 08/05/00 mjn Added pParent to DNSendGroupMessage and DNSendMessage()
* 08/06/00 mjn Added CWorkerJob
* 08/08/00 mjn Mark groups created after CREATE_GROUP
* 08/15/00 rmt Bug #42506 - DPLAY8: LOBBY: Automatic connection settings not being sent
* 08/25/00 mjn Perform queued NameTable operations in DNConnectToHost2()
* 08/28/00 mjn Only compare major version numbers in DNHostVerifyConnect()
* 09/04/00 mjn Added CApplicationDesc
* 09/05/00 mjn Set NameTable DPNID mask when connecting
* 09/13/00 mjn Perform queued operations after creating group in DNConnectToHost2()
* 09/17/00 mjn Split CNameTable.m_bilinkEntries into m_bilinkPlayers and m_bilinkGroups
* 09/26/00 mjn Removed locking from CNameTable::SetVersion() and CNameTable::GetNewVersion()
* 09/27/00 mjn ACK nametable to Host after creating groups and local and host players
* 10/11/00 mjn Fixed up DNAbortConnect() and use it instead of DNConnectToHostFailed()
* 10/17/00 mjn Fixed clean up for unreachable players
* 01/25/01 mjn Fixed 64-bit alignment problem in received messages
* 02/12/01 mjn Fixed CConnection::GetEndPt() to track calling thread
* 03/30/01 mjn Changes to prevent multiple loading/unloading of SP's
* 04/05/01 mjn Call DNHostDisconnect() with DPNDESTROYPLAYERREASON_NORMAL in DNHostConnect1()
* 05/07/01 vpo Whistler 384350: "DPLAY8: CORE: Messages from server can be indicated before connect completes"
* 05/22/01 mjn Properly set DirectNetObject as CONNECTED for successful client connect
* 06/03/01 mjn Don't clean up connect parent from DirectNetObject in DNAbortConnect() (will be done in DNTerminateSession())
* 06/08/01 mjn Disconnect connection in DNConnectToHostFailed()
* 06/25/01 mjn Use connect address for host if missing when installing name table in DNConnectToHost2()
* 07/24/01 mjn Added DPNBUILD_NOSERVER compile flag
*@@END_MSINTERNAL
*
***************************************************************************/
#include "dncorei.h"
//
// Accept a connection from a NewPlayer as the Host in peer-to-peer, or as the Server in
// Client-Server modes.
//
// The connection process is a multi part affair, requiring a bit of
// synchronization between the Host and the NewPlayer.
//
// In the first part:
// The Host waits for the NewPlayer to send player game info
// The Host verifies that everything is in order
// If everything is okay:
// The Host assigns the NewPlayer a DNID,
// Adds the NewPlayer to the Host's NameTable,
// Sends the NameTable to the NewPlayer,
// Sends an ADD_PLAYER message to existing players to add NewPlayer to their NameTable's
// Otherwise:
// Inform NewPlayer that connection process failed
//
// In the second part:
// The Host awaits confirmation from the NewPlayer that the table was received AND installed.
// The Host instructs existing players to connect to the NewPlayer
//
// DNHostConnect1
//
// Called once the connecting player has sent his player info.
//
// - Verify NewPlayer and application info
// - Assign a DNID to NewPlayer
// - Add NewPlayer to Host's NameTable
// - Send Name table to NewPlayer
// - Send ADD_PLAYER message to existing players
//
// LPVOID lpvData Player and application info
// DWORD dwBufferSize Size of player and application info
// HANDLE hEndPt End point handle
#undef DPF_MODNAME
#define DPF_MODNAME "DNHostConnect1"
HRESULT DNHostConnect1(DIRECTNETOBJECT *const pdnObject,
const PVOID pvBuffer,
const DWORD dwBufferSize,
CConnection *const pConnection)
{
HRESULT hResultCode;
UNALIGNED WCHAR *pwszName;
UNALIGNED WCHAR *pwszPassword;
PVOID pvData;
PVOID pvConnectData;
UNALIGNED DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO *pInfo;
CNameTableEntry *pNTEntry;
CNameTableEntry *pAllPlayersGroup;
CPackedBuffer packedBuffer;
CRefCountBuffer *pRefCountBuffer;
CWorkerJob *pWorkerJob;
IDirectPlay8Address *pAddress;
CPackedBuffer PackedBuffer;
void *pvPlayerContext;
BOOL bPlayerVerified;
BOOL fDisconnect;
HANDLE hEndPt;
void *pvReplyBuffer;
DWORD dwReplyBufferSize;
void *pvReplyBufferContext;
CCallbackThread CallbackThread;
#ifdef DBG
TCHAR DP8ABuffer[512] = {0};
DWORD DP8ASize;
#endif // DBG
DPFX(DPFPREP, 4,"Parameters: pvBuffer [0x%p], dwBufferSize [%ld], pConnection [0x%p]",pvBuffer,dwBufferSize,pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pvBuffer != NULL);
DNASSERT(pConnection != NULL);
pAddress = NULL;
pRefCountBuffer = NULL;
pNTEntry = NULL;
pAllPlayersGroup = NULL;
pWorkerJob = NULL;
bPlayerVerified = FALSE;
pvReplyBuffer = NULL;
dwReplyBufferSize = 0;
pvReplyBufferContext = NULL;
CallbackThread.Initialize();
//
// Extract player and application info
//
pInfo = static_cast<DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO*>(pvBuffer);
if (pInfo->dwNameOffset)
{
pwszName = reinterpret_cast<UNALIGNED WCHAR*>(static_cast<BYTE*>(pvBuffer) + pInfo->dwNameOffset);
}
else
{
pwszName = NULL;
}
if (pInfo->dwPasswordOffset)
{
pwszPassword = reinterpret_cast<UNALIGNED WCHAR*>(static_cast<BYTE*>(pvBuffer) + pInfo->dwPasswordOffset);
}
else
{
pwszPassword = NULL;
}
if (pInfo->dwDataOffset)
{
pvData = static_cast<void*>(static_cast<BYTE*>(pvBuffer) + pInfo->dwDataOffset);
}
else
{
pvData = NULL;
}
if (pInfo->dwConnectDataOffset)
{
pvConnectData = (PVOID)((char *)pvBuffer + pInfo->dwConnectDataOffset);
}
else
{
pvConnectData = NULL;
}
if (pInfo->dwURLOffset)
{
#ifdef DPNBUILD_LIBINTERFACE
hResultCode = DP8ACF_CreateInstance(IID_IDirectPlay8Address,
reinterpret_cast<void**>(&pAddress));
#else // ! DPNBUILD_LIBINTERFACE
hResultCode = COM_CoCreateInstance(CLSID_DirectPlay8Address,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDirectPlay8Address,
reinterpret_cast<void**>(&pAddress),
FALSE);
#endif // ! DPNBUILD_LIBINTERFACE
if (hResultCode != S_OK)
{
DPFERR("Could not create IDirectPlay8Address");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
return(hResultCode);
}
DPFX(DPFPREP, 5,"Connecting Player URL [%hs]",static_cast<char*>(pvBuffer) + pInfo->dwURLOffset);
if ((hResultCode = IDirectPlay8Address_BuildFromURLA( pAddress,
static_cast<char*>(pvBuffer) + pInfo->dwURLOffset )) != DPN_OK)
{
DPFERR("Could not build IDirectPlay8Address");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
}
else
{
DPFX(DPFPREP, 5,"No address URL specified - Host will have to determine it");
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
{
DPFERR("Could not retrieve EndPoint from Connection");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
hResultCode = DNGetClearAddress(pdnObject,hEndPt,&pAddress,TRUE);
pConnection->ReleaseEndPt(&CallbackThread);
if (hResultCode != DPN_OK)
{
DPFERR("Could not determine Clear Address");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
#ifdef DBG
DP8ASize = 512;
IDirectPlay8Address_GetURL(pAddress,DP8ABuffer,&DP8ASize);
DPFX(DPFPREP, 5,"Remote Address [%s]",DP8ABuffer);
#endif // DBG
}
#ifdef DBG
if (pwszName != NULL)
{
if ((((DWORD_PTR) pwszName) % sizeof (WCHAR)) == 0)
{
DPFX(DPFPREP, 5,"Connecting Player: Name [%ls], Data Size [%ld], Connect Data Size [%ld]",
pwszName,pInfo->dwDataSize,pInfo->dwConnectDataSize);
}
else
{
DPFX(DPFPREP, 5,"Connecting Player: Name [unaligned_name], Data Size [%ld], Connect Data Size [%ld]",
pInfo->dwDataSize,pInfo->dwConnectDataSize);
}
}
else
{
DPFX(DPFPREP, 5,"Connecting Player: No name, Data Size [%ld], Connect Data Size [%ld]",
pInfo->dwDataSize,pInfo->dwConnectDataSize);
}
#endif // DBG
//
// Avoid alignment errors
//
GUID guidApplication;
GUID guidInstance;
guidApplication = pInfo->guidApplication;
guidInstance = pInfo->guidInstance;
//
// Ensure this connect is valid
//
if ((hResultCode = DNHostVerifyConnect( pdnObject,
pConnection,
pInfo->dwFlags,
pInfo->dwDNETVersion,
pwszPassword,
&guidApplication,
&guidInstance,
pvConnectData,
pInfo->dwConnectDataSize,
pAddress,
&pvPlayerContext,
&pvReplyBuffer,
&dwReplyBufferSize,
&pvReplyBufferContext)) != DPN_OK)
{
DPFX(DPFPREP, 5,"Connect failed, hResultCode = [0x%lx]",hResultCode);
//
// Disconnect this connection. We will also remove it from the indicated list.
//
DNEnterCriticalSection(&pdnObject->csConnectionList);
if (!pConnection->m_bilinkIndicated.IsEmpty())
{
pConnection->Release();
}
pConnection->m_bilinkIndicated.RemoveFromList();
DNLeaveCriticalSection(&pdnObject->csConnectionList);
pConnection->Disconnect(); // Terminate this connection
hResultCode = DPN_OK; // We handled everything okay !
goto Failure; // For clean up
}
bPlayerVerified = TRUE;
//
// I am assuming that the player count has been updated by HostVerifyConnect.
// That means that until we flag the connection as CONNECTed, we will manually
// have to decrement it.
//
//
// Create player entry
//
if ((hResultCode = NameTableEntryNew(pdnObject,&pNTEntry)) != DPN_OK)
{
DPFERR("Could not get new NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
// This function takes the lock internally
pNTEntry->UpdateEntryInfo( pwszName,
pInfo->dwNameSize,
pvData,
pInfo->dwDataSize,
DPNINFO_NAME | DPNINFO_DATA,
FALSE);
pNTEntry->SetDNETVersion( pInfo->dwDNETVersion );
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
pNTEntry->MakePeer();
}
else
{
pNTEntry->MakeClient();
}
if (pvPlayerContext)
{
pNTEntry->SetContext(pvPlayerContext);
}
pNTEntry->StartConnecting();
pNTEntry->SetIndicated();
pNTEntry->NotifyAddRef();
pNTEntry->SetAddress(pAddress);
IDirectPlay8Address_Release(pAddress);
pAddress = NULL;
//
// Add player to NameTable
//
if ((hResultCode = pdnObject->NameTable.AddEntry(pNTEntry)) != DPN_OK)
{
pdnObject->NameTable.Unlock();
DPFERR("Could not add entry to NameTable");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Set up connection
// The connection should be "CONNECTING" or a disconnect has been issued since the NewPlayer sent
// their connect info. Once the DPNID is set on the connection, the standard disconnect handler
// will take care of clean up. If the DPNID is NOT set on the connection, the disconnect code
// will just release the connection, without cleaning up the NameTable or the NameTableEntry.
// Since the NameTable version may have changed since the NewPlayer was added, we will need to
// delete the player from the NameTable (and generate a new version number). We will flag this
// case, and just send out the DELETE_PLAYER message after sending out the ADD_PLAYER
//
pConnection->Lock();
if (pConnection->IsConnecting())
{
pConnection->SetStatus( CONNECTED );
fDisconnect = FALSE;
}
else
{
DPFX(DPFPREP, 5,"NewPlayer has disconnected while joining - send out ADD_PLAYER and then DELETE_PLAYER");
fDisconnect = TRUE;
}
pConnection->SetDPNID( pNTEntry->GetDPNID() );
pNTEntry->SetConnection( pConnection );
pConnection->Unlock();
//
// Now that this connection is part of the NameTableEntry, remove it from the indicated list.
//
DNEnterCriticalSection(&pdnObject->csConnectionList);
if (!pConnection->m_bilinkIndicated.IsEmpty())
{
pConnection->Release();
}
pConnection->m_bilinkIndicated.RemoveFromList();
DNLeaveCriticalSection(&pdnObject->csConnectionList);
//
// Send name table to player
//
if (!fDisconnect)
{
hResultCode = DNSendConnectInfo(pdnObject,pNTEntry,pConnection,pvReplyBuffer,dwReplyBufferSize);
if (hResultCode != DPN_OK && hResultCode != DPNERR_PENDING)
{
DPFERR("Could not send name table to player");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
#pragma TODO(minara,"Clean up here")
goto Failure;
}
}
if (pvReplyBuffer)
{
DNUserReturnBuffer(pdnObject,DPN_OK,pvReplyBuffer,pvReplyBufferContext);
pvReplyBuffer = NULL;
dwReplyBufferSize = 0;
pvReplyBufferContext = NULL;
}
//
// Setup name table entry to be passed to other players
//
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
packedBuffer.Initialize(NULL,0);
if ((hResultCode = pNTEntry->PackEntryInfo(&packedBuffer)) != DPNERR_BUFFERTOOSMALL)
{
DPFERR("Unknown error encountered trying to pack NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
if ((hResultCode = RefCountBufferNew(pdnObject,packedBuffer.GetSizeRequired(),MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not create new RefCountBuffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
packedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(),pRefCountBuffer->GetBufferSize());
if ((hResultCode = pNTEntry->PackEntryInfo(&packedBuffer)) != DPN_OK)
{
DPFERR("Could not pack NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
//
// Send ADD_PLAYER messages to other players (with lower versions)
//
if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
{
DPFERR("Could not create worker thread job (add player)");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pWorkerJob->SetJobType( WORKER_JOB_SEND_NAMETABLE_OPERATION );
pWorkerJob->SetSendNameTableOperationMsgId( DN_MSG_INTERNAL_ADD_PLAYER );
pWorkerJob->SetSendNameTableOperationVersion( pNTEntry->GetVersion() );
pWorkerJob->SetSendNameTableOperationDPNIDExclude( 0 );
pWorkerJob->SetRefCountBuffer( pRefCountBuffer );
DNQueueWorkerJob(pdnObject,pWorkerJob);
pWorkerJob = NULL;
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
//
// If we were in the process of disconnecting, we will need to clean up now
//
if (fDisconnect)
{
DNHostDisconnect(pdnObject,pNTEntry->GetDPNID(),DPNDESTROYPLAYERREASON_NORMAL);
}
pNTEntry->Release();
pNTEntry = NULL;
// Now, wait for synchronization (player has loaded name table)
hResultCode = DPN_OK;
Exit:
CallbackThread.Deinitialize();
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pvReplyBuffer)
{
//
// Return buffer to HostPlayer
//
DNUserReturnBuffer(pdnObject,DPN_OK,pvReplyBuffer,pvReplyBufferContext);
pvReplyBuffer = NULL;
dwReplyBufferSize = 0;
pvReplyBufferContext = NULL;
}
if (bPlayerVerified)
{
pdnObject->ApplicationDesc.DecPlayerCount();
}
if (pAddress)
{
IDirectPlay8Address_Release(pAddress);
pAddress = NULL;
}
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
if (pAllPlayersGroup)
{
pAllPlayersGroup->Release();
pAllPlayersGroup = NULL;
}
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
goto Exit;
}
// DNHostConnect2
//
// Mark player as "available" in NameTable
// Send INSTRUCT_CONNECT messages to the existing players to add new player
//
// CConnection *pConnection Connection for connecting player
//
#undef DPF_MODNAME
#define DPF_MODNAME "DNHostConnect2"
HRESULT DNHostConnect2(DIRECTNETOBJECT *const pdnObject,
CConnection *const pConnection)
{
HRESULT hResultCode;
DPNID dpnid;
CRefCountBuffer *pRefCountBuffer;
CNameTableEntry *pNTEntry;
CWorkerJob *pWorkerJob;
DN_INTERNAL_MESSAGE_INSTRUCT_CONNECT *pInfo;
DPFX(DPFPREP, 4,"Parameters: pConnection [0x%p]",pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pConnection != NULL);
pRefCountBuffer = NULL;
pNTEntry = NULL;
pWorkerJob = NULL;
pConnection->Lock();
dpnid = pConnection->GetDPNID();
pConnection->Unlock();
pdnObject->NameTable.PopulateConnection(pConnection);
//
// Instruct existing players to connect to NewPlayer
//
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
DPFX(DPFPREP, 5,"Instruct existing players to connect to NewPlayer [0x%lx]",dpnid);
// Need version number of this player
if ((hResultCode = pdnObject->NameTable.FindEntry(dpnid,&pNTEntry)) != DPN_OK)
{
DPFERR("Could not find player in nametable");
DisplayDNError(0,hResultCode);
// DNASSERT(FALSE);
goto Failure;
}
hResultCode = RefCountBufferNew(pdnObject,
sizeof(DN_INTERNAL_MESSAGE_INSTRUCT_CONNECT),
MemoryBlockAlloc,
MemoryBlockFree,
&pRefCountBuffer);
if (hResultCode != DPN_OK)
{
DPFERR("Could not create CountBuffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo = reinterpret_cast<DN_INTERNAL_MESSAGE_INSTRUCT_CONNECT*>(pRefCountBuffer->GetBufferAddress());
pInfo->dpnid = dpnid;
pdnObject->NameTable.WriteLock();
pdnObject->NameTable.GetNewVersion( &pInfo->dwVersion );
pdnObject->NameTable.Unlock();
pInfo->dwVersionNotUsed = 0;
if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
{
DPFERR("Could not create worker thread job (add player)");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pWorkerJob->SetJobType( WORKER_JOB_SEND_NAMETABLE_OPERATION );
pWorkerJob->SetSendNameTableOperationMsgId( DN_MSG_INTERNAL_INSTRUCT_CONNECT );
pWorkerJob->SetSendNameTableOperationVersion( pInfo->dwVersion );
pWorkerJob->SetSendNameTableOperationDPNIDExclude( 0 );
pWorkerJob->SetRefCountBuffer( pRefCountBuffer );
DNQueueWorkerJob(pdnObject,pWorkerJob);
pWorkerJob = NULL;
pNTEntry->Release();
pNTEntry = NULL;
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
goto Exit;
}
// DNHostVerifyConnect
//
// Host connection verification. Ensure that the player connecting meets ALL criteria
// including:
// correct mode (client/server or peer/peer)
// correct instance guid
// correct application (if specified)
// correct password (if specified)
// correct user spec (through call-back)
//
#undef DPF_MODNAME
#define DPF_MODNAME "DNHostVerifyConnect"
HRESULT DNHostVerifyConnect(DIRECTNETOBJECT *const pdnObject,
CConnection *const pConnection,
const DWORD dwFlags,
const DWORD dwDNETVersion,
UNALIGNED WCHAR *const pwszPassword,
GUID *const pguidApplication,
GUID *const pguidInstance,
PVOID const pvConnectData,
const DWORD dwConnectDataSize,
IDirectPlay8Address *const pAddress,
void **const ppvPlayerContext,
void **const ppvReplyBuffer,
DWORD *const pdwReplyBufferSize,
void **const ppvReplyBufferContext)
{
HRESULT hResultCode;
HRESULT hrFailure;
PVOID pvReplyData;
DWORD dwReplyDataSize;
PVOID pvReplyContext;
DWORD dwBufferSize;
HANDLE hEndPt;
CNameTableEntry *pLocalPlayer;
CRefCountBuffer *pRefCountBuffer;
CPackedBuffer packedBuffer;
IDirectPlay8Address *pDevice;
DN_INTERNAL_MESSAGE_CONNECT_FAILED *pInfo;
BOOL fDecPlayerCount;
CCallbackThread CallbackThread;
GUID guidnull;
#ifdef DBG
TCHAR DP8ABuffer[512] = {0};
DWORD DP8ASize;
#endif // DBG
DPFX(DPFPREP, 6,"Parameters: dwFlags [0x%lx], dwDPlay8Version [0x%lx], pwszPassword [0x%p], pguidApplication [0x%p], pguidInstance [0x%p], pvConnectData [0x%p], dwConnectDataSize [%ld]",
dwFlags,dwDNETVersion,pwszPassword,pguidApplication,pguidInstance,pvConnectData,dwConnectDataSize);
DNASSERT(pdnObject != NULL);
DNASSERT(ppvReplyBuffer != NULL);
DNASSERT(pdwReplyBufferSize != NULL);
DNASSERT(ppvReplyBufferContext != NULL);
pLocalPlayer = NULL;
pRefCountBuffer = NULL;
pvReplyData = NULL;
dwReplyDataSize = 0;
pvReplyContext = NULL;
pDevice = NULL;
fDecPlayerCount = FALSE;
memset(&guidnull, 0, sizeof(guidnull));
CallbackThread.Initialize();
//
// Ensure we're not closing or host migrating
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING)
{
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
hResultCode = DPNERR_ALREADYCLOSING;
goto CleanUp;
}
if (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING)
{
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
hResultCode = DPNERR_NOTHOST;
goto Failure;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
//
// Ensure we are the Host
//
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
{
DPFERR("Connection received by non-host player");
hResultCode = DPNERR_NOTHOST;
goto Failure;
}
if (!pLocalPlayer->IsHost())
{
DPFERR("Connection received by non-host player");
hResultCode = DPNERR_NOTHOST;
goto Failure;
}
pLocalPlayer->Release();
pLocalPlayer = NULL;
//
// Verify Mode
//
if ((pdnObject->dwFlags & DN_OBJECT_FLAG_PEER) && !(dwFlags & DN_OBJECT_FLAG_PEER))
{
DPFX(DPFPREP, 7,"Non peer player attempting connection to peer");
hResultCode = DPNERR_INVALIDINTERFACE;
goto Failure;
}
#ifndef DPNBUILD_NOSERVER
if ((pdnObject->dwFlags & DN_OBJECT_FLAG_SERVER) && !(dwFlags & DN_OBJECT_FLAG_CLIENT))
{
DPFX(DPFPREP, 7,"Non client player attempting connection to server");
hResultCode = DPNERR_INVALIDINTERFACE;
goto Failure;
}
#endif // DPNBUILD_NOSERVER
//
// Verify DNET version - we will only compare the high 16-bits (major number)
// - we will allow different low 16-bits (minor number)
//
if ((dwDNETVersion & 0xffff0000) != (DN_VERSION_CURRENT & 0xffff0000))
{
DPFX(DPFPREP, 7,"Invalid DPlay8 version!");
hResultCode = DPNERR_INVALIDVERSION;
goto Failure;
}
//
// Validate instance GUID
//
if (pguidInstance && (*pguidInstance != guidnull))
{
if (!pdnObject->ApplicationDesc.IsEqualInstanceGuid(pguidInstance))
{
DPFERR("Invalid Instance GUID specified at connection");
hResultCode = DPNERR_INVALIDINSTANCE;
goto Failure;
}
}
//
// Validate application (if specified)
//
if (pguidApplication && (*pguidApplication != guidnull))
{
if (!pdnObject->ApplicationDesc.IsEqualApplicationGuid(pguidApplication))
{
DPFERR("Invalid Application GUID specified at connection");
hResultCode = DPNERR_INVALIDAPPLICATION;
goto Failure;
}
}
//
// Validate password
//
if (!pdnObject->ApplicationDesc.IsEqualPassword(pwszPassword))
{
DPFERR("Incorrect password (required) specified at connection");
hResultCode = DPNERR_INVALIDPASSWORD;
goto Failure;
}
//
// Get device address this connection came in on
//
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
{
DPFERR("Could not extract endpoint from CConnection");
DisplayDNError(0,hResultCode);
hResultCode = DPNERR_GENERIC; // Is there a better one ?
goto Failure;
}
hResultCode = DNGetLocalDeviceAddress(pdnObject,hEndPt,&pDevice);
pConnection->ReleaseEndPt(&CallbackThread);
if (hResultCode != DPN_OK)
{
DPFERR("Could not determine local device address");
DisplayDNError(0,hResultCode);
hResultCode = DPNERR_GENERIC; // Is there a better one ?
goto Failure;
}
#ifdef DBG
DP8ASize = 512;
IDirectPlay8Address_GetURL(pDevice,DP8ABuffer,&DP8ASize);
DPFX(DPFPREP, 5,"Local Device Address [%s]",DP8ABuffer);
#endif // DBG
//
// Increment AppDesc count
//
hResultCode = pdnObject->ApplicationDesc.IncPlayerCount( TRUE );
if (hResultCode != DPN_OK)
{
DPFX(DPFPREP, 7,"Could not add player to game");
DisplayDNError(0,hResultCode);
goto Failure;
}
fDecPlayerCount = TRUE; // only for error handling
//
// Validate user specified data (through call-back)
//
if (pvConnectData)
{
DPFX(DPFPREP, 7,"dwConnectDataSize [%ld]",dwConnectDataSize);
}
else
{
DPFX(DPFPREP, 7,"No connect data given");
}
if ((hResultCode = DNUserIndicateConnect( pdnObject,
pvConnectData,
dwConnectDataSize,
&pvReplyData,
&dwReplyDataSize,
&pvReplyContext,
pAddress,
pDevice,
ppvPlayerContext)) != DPN_OK)
{
DPFERR("Application declined connection attempt");
hResultCode = DPNERR_HOSTREJECTEDCONNECTION;
goto Failure;
}
IDirectPlay8Address_Release(pDevice);
pDevice = NULL;
//
// Save reply buffer
//
if ((pvReplyData) && (dwReplyDataSize != 0))
{
*ppvReplyBuffer = pvReplyData;
*pdwReplyBufferSize = dwReplyDataSize;
*ppvReplyBufferContext = pvReplyContext;
}
else
{
*ppvReplyBuffer = NULL;
*pdwReplyBufferSize = 0;
*ppvReplyBufferContext = NULL;
}
Exit:
CallbackThread.Deinitialize();
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (fDecPlayerCount)
{
pdnObject->ApplicationDesc.DecPlayerCount();
}
//
// Send a message back to the connecting player that this failed
//
DPFX(DPFPREP, 7,"Connect failed [0x%lx]",hResultCode);
hrFailure = hResultCode;
if (pvReplyData == NULL)
{
dwReplyDataSize = 0; // basic validation
}
dwBufferSize = sizeof(DN_INTERNAL_MESSAGE_CONNECT_FAILED) + dwReplyDataSize;
DPFX(DPFPREP, 7,"Failure buffer is [%ld] bytes",dwBufferSize);
//
// Create and fill failure message buffer
//
if ((hResultCode = RefCountBufferNew(pdnObject,dwBufferSize,MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not create RefCountBuffer");
DisplayDNError(0,hResultCode);
goto CleanUp;
}
packedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(),pRefCountBuffer->GetBufferSize());
pInfo = reinterpret_cast<DN_INTERNAL_MESSAGE_CONNECT_FAILED*>(pRefCountBuffer->GetBufferAddress());
if ((hResultCode = packedBuffer.AddToFront(NULL,sizeof(DN_INTERNAL_MESSAGE_CONNECT_FAILED))) != DPN_OK)
{
DPFERR("Could not add header to message buffer");
DisplayDNError(0,hResultCode);
goto CleanUp;
}
if (pvReplyData)
{
if ((hResultCode = packedBuffer.AddToBack(pvReplyData,dwReplyDataSize)) != DPN_OK)
{
DPFERR("Could not add reply to failure buffer");
DisplayDNError(0,hResultCode);
goto CleanUp;
}
pInfo->dwReplyOffset = packedBuffer.GetTailOffset();
pInfo->dwReplySize = dwReplyDataSize;
DNUserReturnBuffer(pdnObject,DPN_OK,pvReplyData,pvReplyContext); // Return buffer
}
else
{
pInfo->dwReplyOffset = 0;
pInfo->dwReplySize = 0;
}
pInfo->hResultCode = hrFailure;
//
// Send failure message
//
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_CONNECT_FAILED,
NULL,
pRefCountBuffer->BufferDescAddress(),
1,
pRefCountBuffer,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
CleanUp:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
if (pLocalPlayer)
{
pLocalPlayer->Release();
pLocalPlayer = NULL;
}
if (pDevice)
{
IDirectPlay8Address_Release(pDevice);
pDevice = NULL;
}
goto Exit;
}
//
// DNHostDropPlayer
//
// An existing player in a peer-peer game could not connect to a NewPlayer and is informing the
// Host of this situation. As a result, the Host will drop inform the NewPlayer of this and then
// drop the NewPlayer from the game
//
#undef DPF_MODNAME
#define DPF_MODNAME "DNHostDropPlayer"
HRESULT DNHostDropPlayer(DIRECTNETOBJECT *const pdnObject,
const DPNID dpnid, // ExistingPlayer who could not connect to NewPlayer
void *const pvBuffer)
{
HRESULT hResultCode;
CRefCountBuffer *pRefCountBuffer;
CNameTableEntry *pNTEntry;
CConnection *pConnection;
UNALIGNED DN_INTERNAL_MESSAGE_INSTRUCTED_CONNECT_FAILED *pInfo;
DN_INTERNAL_MESSAGE_CONNECT_ATTEMPT_FAILED *pMsg;
DPFX(DPFPREP, 4,"Parameters: pvBuffer [0x%p]",pvBuffer);
DNASSERT(pdnObject != NULL);
DNASSERT(pvBuffer != NULL);
pRefCountBuffer = NULL;
pNTEntry = NULL;
pConnection = NULL;
pInfo = static_cast<DN_INTERNAL_MESSAGE_INSTRUCTED_CONNECT_FAILED*>(pvBuffer);
DPFX(DPFPREP, 5,"Connection to [0x%lx] failed",pInfo->dpnid);
//
// Get connection for NewPlayer
//
if ((hResultCode = pdnObject->NameTable.FindEntry(pInfo->dpnid,&pNTEntry)) != DPN_OK)
{
DPFERR("NewPlayer no longer in NameTable - not to worry");
hResultCode = DPN_OK;
goto Failure;
}
if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) != DPN_OK)
{
DPFERR("Connection for NewPlayer no longer valid - not to worry");
hResultCode = DPN_OK;
goto Failure;
}
pNTEntry->Release();
pNTEntry = NULL;
//
// Send message to NewPlayer informing them of which existing player could not connect to them
//
hResultCode = RefCountBufferNew(pdnObject,
sizeof(DN_INTERNAL_MESSAGE_CONNECT_ATTEMPT_FAILED),
MemoryBlockAlloc,
MemoryBlockFree,
&pRefCountBuffer);
if (hResultCode != DPN_OK)
{
DPFERR("Could not allocate RefCountBuffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pMsg = reinterpret_cast<DN_INTERNAL_MESSAGE_CONNECT_ATTEMPT_FAILED*>(pRefCountBuffer->GetBufferAddress());
pMsg->dpnid = dpnid;
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_CONNECT_ATTEMPT_FAILED,
pInfo->dpnid,
pRefCountBuffer->BufferDescAddress(),
1,
pRefCountBuffer,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
if (hResultCode != DPNERR_PENDING)
{
DPFERR("Could not send message to NewPlayer - assume he is gone or going");
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
hResultCode = DPN_OK;
goto Failure;
}
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
//
// We will just drop (or at least attempt to drop) the connection
//
pConnection->Disconnect();
pConnection->Release();
pConnection = NULL;
hResultCode = DNHostDisconnect(pdnObject,pInfo->dpnid,DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER);
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
if (pConnection)
{
pConnection->Release();
pConnection = NULL;
}
goto Exit;
}
//
// Perform a connection to a host player (either host in peer-to-peer or server in client-server).
// This processes the handshaking of the name table between the host and the player.
// The first step is to send the player and application info to the host for verification
// The second step is to receive and process the name table from the host
// The last step is to wait for connections from other players and populate the name table
// It assumes that the connection to the host has already been initiated.
//
// DNPrepareConnectInfo
//
// Prepare the connection info block which will be sent to the Host player once a connection
// has been established.
#undef DPF_MODNAME
#define DPF_MODNAME "DNPrepareConnectInfo"
HRESULT DNPrepareConnectInfo(DIRECTNETOBJECT *const pdnObject,
CConnection *const pConnection,
CRefCountBuffer **const ppRefCountBuffer)
{
HRESULT hResultCode;
DWORD dwSize;
DWORD dwPasswordSize;
DWORD dwAddressSize;
HANDLE hEndPt;
CRefCountBuffer *pRefCountBuffer;
CPackedBuffer packedBuffer;
IDirectPlay8Address *pAddress;
DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO *pInfo;
CCallbackThread CallbackThread;
#ifdef DBG
TCHAR DP8ABuffer[512] = {0};
DWORD DP8ASize;
#endif // DBG
DPFX(DPFPREP, 4,"Parameters: pConnection [0x%p], ppRefCountBuffer [0x%p]",pConnection,ppRefCountBuffer);
DNASSERT(pdnObject != NULL);
DNASSERT(pConnection != NULL);
DNASSERT(ppRefCountBuffer != NULL);
pRefCountBuffer = NULL;
pAddress = NULL;
dwAddressSize = 0;
CallbackThread.Initialize();
//
// Get clear address
//
dwAddressSize = 0;
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) == DPN_OK)
{
if ((hResultCode = DNGetClearAddress(pdnObject,hEndPt,&pAddress,FALSE)) == DPN_OK)
{
if (pAddress != NULL)
{
// Get address URL size
IDirectPlay8Address_GetURL(pAddress,NULL,&dwAddressSize);
DNASSERT(dwAddressSize != 0);
#ifdef DBG
DP8ASize = 512;
IDirectPlay8Address_GetURL(pAddress,DP8ABuffer,&DP8ASize);
DPFX(DPFPREP, 5,"Remote Address [%s]",DP8ABuffer);
#endif // DBG
}
}
pConnection->ReleaseEndPt(&CallbackThread);
}
// Determine total size of connection info buffer
if (pdnObject->ApplicationDesc.GetPassword() != NULL)
dwPasswordSize = (wcslen(pdnObject->ApplicationDesc.GetPassword()) + 1) * sizeof(WCHAR);
else
dwPasswordSize = 0;
DNASSERT(pdnObject->NameTable.GetDefaultPlayer() != NULL);
dwSize = sizeof(DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO)
+ pdnObject->dwConnectDataSize
+ dwAddressSize
+ dwPasswordSize
+ pdnObject->NameTable.GetDefaultPlayer()->GetDataSize()
+ pdnObject->NameTable.GetDefaultPlayer()->GetNameSize();
// Allocate connection info buffer
DPFX(DPFPREP, 7,"Need to allocate [%ld] bytes",dwSize);
if ((hResultCode = RefCountBufferNew(pdnObject,dwSize,MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not allocate space for connection info");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
packedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(),pRefCountBuffer->GetBufferSize());
pInfo = static_cast<DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO*>(packedBuffer.GetHeadAddress());
// Type of interface
#ifndef DPNBUILD_NOSERVER
pInfo->dwFlags = pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER | DN_OBJECT_FLAG_CLIENT | DN_OBJECT_FLAG_SERVER);
#else
pInfo->dwFlags = pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER | DN_OBJECT_FLAG_CLIENT);
#endif // DPNBUILD_NOSERVER
// Version of DIRECTNET
pInfo->dwDNETVersion = DN_VERSION_CURRENT;
// Name
if (pdnObject->NameTable.GetDefaultPlayer()->GetNameSize())
{
if ((hResultCode = packedBuffer.AddToBack(pdnObject->NameTable.GetDefaultPlayer()->GetName(),
pdnObject->NameTable.GetDefaultPlayer()->GetNameSize())) != DPN_OK)
{
DPFERR("Could not add name to connection buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo->dwNameOffset = packedBuffer.GetTailOffset();
pInfo->dwNameSize = pdnObject->NameTable.GetDefaultPlayer()->GetNameSize();
}
else
{
pInfo->dwNameOffset = 0;
pInfo->dwNameSize = 0;
}
// Player data
if (pdnObject->NameTable.GetDefaultPlayer()->GetData() && pdnObject->NameTable.GetDefaultPlayer()->GetDataSize())
{
if ((hResultCode = packedBuffer.AddToBack(pdnObject->NameTable.GetDefaultPlayer()->GetData(),
pdnObject->NameTable.GetDefaultPlayer()->GetDataSize())) != DPN_OK)
{
DPFERR("Could not add connect data to connection buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo->dwDataOffset = packedBuffer.GetTailOffset();
pInfo->dwDataSize = pdnObject->NameTable.GetDefaultPlayer()->GetDataSize();
}
else
{
pInfo->dwDataOffset = 0;
pInfo->dwDataSize = 0;
}
// Password
if (dwPasswordSize)
{
if ((hResultCode = packedBuffer.AddToBack(pdnObject->ApplicationDesc.GetPassword(),dwPasswordSize)) != DPN_OK)
{
DPFERR("Could not add password to connection buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo->dwPasswordOffset = packedBuffer.GetTailOffset();
pInfo->dwPasswordSize = dwPasswordSize;
}
else
{
pInfo->dwPasswordOffset = 0;
pInfo->dwPasswordSize = 0;
}
// Connect data
if (pdnObject->pvConnectData)
{
if ((hResultCode = packedBuffer.AddToBack(pdnObject->pvConnectData,pdnObject->dwConnectDataSize)) != DPN_OK)
{
DPFERR("Could not add connect data to connection buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo->dwConnectDataOffset = packedBuffer.GetTailOffset();
pInfo->dwConnectDataSize = pdnObject->dwConnectDataSize;
}
else
{
pInfo->dwConnectDataOffset = 0;
pInfo->dwConnectDataSize = 0;
}
// Clear address URL
if (dwAddressSize)
{
if ((hResultCode = packedBuffer.AddToBack(NULL,dwAddressSize)) != DPN_OK)
{
DPFERR("Could not add address URL to connection buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
if ((hResultCode = IDirectPlay8Address_GetURLA(pAddress,
static_cast<char*>(packedBuffer.GetTailAddress()),
&dwAddressSize)) != DPN_OK)
{
DPFERR("Could not get address URL");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pInfo->dwURLOffset = packedBuffer.GetTailOffset();
pInfo->dwURLSize = dwAddressSize;
}
else
{
pInfo->dwURLOffset = 0;
pInfo->dwURLSize = 0;
}
// Instance and appplication GUIDs
memcpy(&pInfo->guidInstance,pdnObject->ApplicationDesc.GetInstanceGuid(),sizeof(GUID));
memcpy(&pInfo->guidApplication,pdnObject->ApplicationDesc.GetApplicationGuid(),sizeof(GUID));
*ppRefCountBuffer = pRefCountBuffer;
pRefCountBuffer = NULL;
if (pAddress)
{
IDirectPlay8Address_Release(pAddress);
pAddress = NULL;
}
hResultCode = DPN_OK;
Exit:
CallbackThread.Deinitialize();
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
if (pAddress)
{
IDirectPlay8Address_Release(pAddress);
pAddress = NULL;
}
goto Exit;
}
// DNConnectToHost1
//
// After receiving protocol level connection acknowledgement, send player and application info
// CConnection *pConnection Connection to host
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToHost1"
HRESULT DNConnectToHost1(DIRECTNETOBJECT *const pdnObject,
CConnection *const pConnection)
{
HRESULT hResultCode;
CRefCountBuffer *pRefCountBuffer;
CAsyncOp *pAsyncOp;
CAsyncOp *pConnectParent;
DPFX(DPFPREP, 4,"Parameters: pConnection [0x%p]",pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pConnection != NULL);
pRefCountBuffer = NULL;
pAsyncOp = NULL;
pConnectParent = NULL;
//
// Get connect parent (and make sure we're not closing)
// We will also store the CConnection on the connect parent for future clean up.
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_CLOSING | DN_OBJECT_FLAG_DISCONNECTING))
{
DPFERR("Object is CLOSING or DISCONNECTING");
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
pConnection->Disconnect();
hResultCode = DPN_OK;
goto Failure;
}
DNASSERT(pdnObject->pConnectParent != NULL);
pdnObject->pConnectParent->SetConnection( pConnection );
pdnObject->pConnectParent->AddRef();
pConnectParent = pdnObject->pConnectParent;
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
//
// Prepare connect info
//
if ((hResultCode = DNPrepareConnectInfo(pdnObject,pConnection,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not prepare connect info");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
DNASSERT(pRefCountBuffer != NULL);
//
// Send connect info
//
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_PLAYER_CONNECT_INFO,
0,
pRefCountBuffer->BufferDescAddress(),
1,
pRefCountBuffer,
0,
DN_SENDFLAGS_RELIABLE,
pConnectParent,
&pAsyncOp);
if (hResultCode == DPNERR_PENDING)
{
pAsyncOp->SetCompletion( DNCompleteSendConnectInfo );
pAsyncOp->Release();
pAsyncOp = NULL;
hResultCode = DPN_OK;
}
else
{
//
// Save error code, clean up DirectNetObject and fail
//
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
DNAbortConnect(pdnObject,hResultCode);
}
pConnectParent->Release();
pConnectParent = NULL;
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pConnectParent)
{
pConnectParent->Release();
pConnectParent = NULL;
}
goto Exit;
}
// DN_ConnectToHost2
//
// Extract and install the host supplied name table
// Send name table acknowledgement to the host
// Propegate ADD_PLAYER messages to the application for host and local players
// Propegate CREATE_GROUP messages to the application for groups in the name table
//
// PVOID pvData NameTable buffer
// CConnection *pConnection Connection to host
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToHost2"
HRESULT DNConnectToHost2(DIRECTNETOBJECT *const pdnObject,
const PVOID pvData,
CConnection *const pConnection)
{
HRESULT hResultCode;
CNameTableEntry *pNTEntry;
CNameTableEntry *pHostPlayer;
CNameTableEntry *pLocalPlayer;
CNameTableOp *pNTOp;
CBilink *pBilink;
DPNID dpnid;
DWORD dwFlags;
CConnection *pLocalConnection;
BOOL fNotify;
IDirectPlay8Address *pIDevice;
IDirectPlay8Address *pIHost;
CAsyncOp *pListenParent;
CAsyncOp *pConnectParent;
HANDLE hEndPt;
CCallbackThread CallbackThread;
DPFX(DPFPREP, 4,"Parameters: pvData [0x%p], pConnection [0x%p]",pvData,pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pvData != NULL);
DNASSERT(pConnection != NULL);
pNTEntry = NULL;
pHostPlayer = NULL;
pLocalPlayer = NULL;
pLocalConnection = NULL;
pIDevice = NULL;
pIHost = NULL;
pListenParent = NULL;
pConnectParent = NULL;
CallbackThread.Initialize();
//
// Initial try to catch a close
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_CLOSING | DN_OBJECT_FLAG_DISCONNECTING))
{
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
hResultCode = DPN_OK;
goto Failure;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
//
// Extract application description and name table
//
if ((hResultCode = DNReceiveConnectInfo(pdnObject,pvData,pConnection,&dpnid)) != DPN_OK)
{
DPFERR("Could not extract name table passed by host");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Get Connection object for local player
//
if ((hResultCode = ConnectionNew(pdnObject,&pLocalConnection)) != DPN_OK)
{
DPFERR("Could not create new Connection");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
//
// Get Host and Local players
//
if ((hResultCode = pdnObject->NameTable.GetHostPlayerRef( &pHostPlayer )) != DPN_OK)
{
DPFERR("Could not find Host player");
DisplayDNError(0,hResultCode);
goto Failure;
}
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
{
DPFERR("Could not find Local player");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Ensure we have an address for the Host player
//
if (pHostPlayer->GetAddress() == NULL)
{
//
// Use connect address if it exists
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->pConnectAddress)
{
IDirectPlay8Address_AddRef(pdnObject->pConnectAddress);
pIHost = pdnObject->pConnectAddress;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
if (pIHost == NULL)
{
//
// No connect address was specified, so we will query for the host's address
//
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
{
DPFERR("Could not get end point from connection");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
hResultCode = DNGetClearAddress(pdnObject,hEndPt,&pIHost,TRUE);
pConnection->ReleaseEndPt(&CallbackThread);
if (hResultCode != DPN_OK)
{
DPFERR("Could not get clear address for Host player");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
}
pHostPlayer->SetAddress(pIHost);
IDirectPlay8Address_Release(pIHost);
pIHost = NULL;
}
//
// Start LISTENs for CONNECTs from existing players in Peer-Peer mode
//
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
if ((hResultCode = pConnection->GetEndPt(&hEndPt,&CallbackThread)) != DPN_OK)
{
DPFERR("Could not get end point from connection");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
hResultCode = DNGetLocalDeviceAddress(pdnObject,hEndPt,&pIDevice);
pConnection->ReleaseEndPt(&CallbackThread);
if (hResultCode != DPN_OK)
{
DPFERR("Could not get LISTEN address from endpoint");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
// Parent Async Op
if ((hResultCode = AsyncOpNew(pdnObject,&pListenParent)) != DPN_OK)
{
DPFERR("Could not create AsyncOp");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pListenParent->SetOpType( ASYNC_OP_LISTEN );
pListenParent->MakeParent();
pListenParent->SetCompletion( DNCompleteListen );
dwFlags = DN_LISTENFLAGS_DISALLOWENUMS;
if (pdnObject->ApplicationDesc.GetReservedDataSize() > 0)
{
dwFlags |= DN_LISTENFLAGS_SESSIONDATA;
}
if (pdnObject->ApplicationDesc.IsFastSigned())
{
dwFlags|=DN_LISTENFLAGS_FASTSIGNED;
}
else if (pdnObject->ApplicationDesc.IsFullSigned())
{
dwFlags|=DN_LISTENFLAGS_FULLSIGNED;
}
pListenParent->SetOpFlags( dwFlags );
// Perform child LISTEN
hResultCode = DNPerformSPListen(pdnObject,
pIDevice,
pListenParent,
NULL);
if (hResultCode != DPN_OK)
{
DPFERR("Could not perform child LISTEN");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
// Store parent LISTEN on DirectNet object
#pragma BUGBUG( minara, "What if we are closing down and have already terminated all listens ?" )
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
pListenParent->AddRef();
pdnObject->pListenParent = pListenParent;
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
pListenParent->Release();
pListenParent = NULL;
IDirectPlay8Address_Release(pIDevice);
pIDevice = NULL;
}
//
// Indicate peer connected - we will perform this before notifying the host so that
// DN_OBJECT_FLAG_CONNECTED is set when existing player CONNECTs come in
//
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_CLOSING | DN_OBJECT_FLAG_DISCONNECTING))
{
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
#pragma TODO( minara, "Shut down listen" )
hResultCode = DPN_OK;
goto Failure;
}
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_CONNECTING);
pdnObject->dwFlags |= DN_OBJECT_FLAG_CONNECTED;
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
//
// Inform application of groups and players.
// We will inform the application of
// - CREATE_GROUP
// - ADD_PLAYER (for Local and Host players)
// - ADD_PLAYER_TO_GROUP
//
pdnObject->NameTable.ReadLock();
pBilink = pdnObject->NameTable.m_bilinkGroups.GetNext();
while (pBilink != &pdnObject->NameTable.m_bilinkGroups)
{
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
pNTEntry->AddRef();
pdnObject->NameTable.Unlock();
fNotify = FALSE;
pNTEntry->Lock();
if (!pNTEntry->IsAvailable() && !pNTEntry->IsDisconnecting() && !pNTEntry->IsAutoDestructGroup())
{
pNTEntry->MakeAvailable();
pNTEntry->NotifyAddRef();
pNTEntry->NotifyAddRef();
pNTEntry->SetInUse();
fNotify = TRUE;
}
pNTEntry->Unlock();
if (fNotify)
{
DNASSERT(!pNTEntry->IsAllPlayersGroup());
DNUserCreateGroup(pdnObject,pNTEntry);
pNTEntry->PerformQueuedOperations();
pdnObject->NameTable.PopulateGroup( pNTEntry );
}
pNTEntry->Release();
pNTEntry = NULL;
pdnObject->NameTable.ReadLock();
if (pBilink->IsEmpty())
{
pBilink = pdnObject->NameTable.m_bilinkGroups.GetNext();
}
else
{
pBilink = pBilink->GetNext();
}
}
pNTEntry = NULL;
pdnObject->NameTable.Unlock();
}
//
// We will pre-set the Host connection, so that any operation from the CREATE_PLAYER notification call-back
// for the local player will be able to find the Host player's connection (to send messages to). We will
// not, however, expose the Host player to the user yet.
//
pHostPlayer->Lock();
pHostPlayer->SetConnection( pConnection );
pHostPlayer->Unlock();
//
// Add Local player
//
pLocalConnection->SetStatus( CONNECTED );
pLocalConnection->SetEndPt(NULL);
pLocalConnection->MakeLocal();
//
// Preset player context
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
DNASSERT(pdnObject->pConnectParent);
if (pdnObject->pConnectParent)
{
pdnObject->pConnectParent->AddRef();
pConnectParent = pdnObject->pConnectParent;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
if (pConnectParent)
{
pConnectParent->Lock();
if (pConnectParent->GetContext())
{
pLocalPlayer->Lock();
pLocalPlayer->SetContext(pConnectParent->GetContext());
pLocalPlayer->Unlock();
}
pConnectParent->SetResult( DPN_OK );
pConnectParent->Unlock();
pConnectParent->Release();
pConnectParent = NULL;
}
#ifndef DPNBUILD_NOSERVER
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER|DN_OBJECT_FLAG_SERVER))
#else
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER))
#endif // DPNBUILD_NOSERVER
{
pdnObject->ApplicationDesc.IncPlayerCount( FALSE );
pLocalConnection->SetDPNID(pLocalPlayer->GetDPNID());
pdnObject->NameTable.PopulateConnection(pLocalConnection);
}
else
{
pLocalPlayer->Lock();
pLocalPlayer->SetConnection(pLocalConnection);
pLocalPlayer->StopConnecting();
pLocalPlayer->MakeAvailable();
pLocalPlayer->Unlock();
pdnObject->NameTable.DecOutstandingConnections();
}
pLocalConnection->Release();
pLocalConnection = NULL;
//
// Add Host player
//
#ifndef DPNBUILD_NOSERVER
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER|DN_OBJECT_FLAG_SERVER))
#else
if (pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER))
#endif // DPNBUILD_NOSERVER
{
pdnObject->ApplicationDesc.IncPlayerCount( FALSE );
//
// If we have lost the connection to the host player, we will abort the connect process
//
pConnection->Lock();
if (!pConnection->IsDisconnecting() && !pConnection->IsInvalid())
{
pConnection->SetDPNID(pHostPlayer->GetDPNID());
pConnection->SetStatus( CONNECTED );
pConnection->Unlock();
pdnObject->NameTable.PopulateConnection(pConnection);
}
else
{
pConnection->Unlock();
DNAbortConnect(pdnObject,DPNERR_CONNECTIONLOST);
goto Failure;
}
}
else
{
pConnection->SetStatus( CONNECTED );
pHostPlayer->Lock();
pHostPlayer->StopConnecting();
//
// We won't make the host player available until after CONNECT_COMPLETE
// has been indicated and the callback has returned.
//
//pHostPlayer->MakeAvailable();
pHostPlayer->Unlock();
pdnObject->NameTable.DecOutstandingConnections();
}
pLocalPlayer->Release();
pLocalPlayer = NULL;
//
// Process any nametable operations that might have arrived
//
pdnObject->NameTable.ReadLock();
pBilink = pdnObject->NameTable.m_bilinkNameTableOps.GetNext();
while (pBilink != &pdnObject->NameTable.m_bilinkNameTableOps)
{
pNTOp = CONTAINING_OBJECT(pBilink,CNameTableOp,m_bilinkNameTableOps);
if ((pNTOp->GetVersion() == (pdnObject->NameTable.GetVersion() + 1))
&& !pNTOp->IsInUse())
{
pNTOp->SetInUse();
pdnObject->NameTable.Unlock();
hResultCode = DNNTPerformOperation( pdnObject,
pNTOp->GetMsgId(),
pNTOp->GetRefCountBuffer()->GetBufferAddress() );
pdnObject->NameTable.ReadLock();
}
else
{
//
// Once we find an operation that we won't perform, there is no point continuing
//
break;
}
pBilink = pBilink->GetNext();
}
pdnObject->NameTable.Unlock();
//
// Send connect info acknowledgement to host
//
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_ACK_CONNECT_INFO,
pHostPlayer->GetDPNID(),
NULL,
0,
NULL,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
if (hResultCode != DPNERR_PENDING)
{
DNAbortConnect(pdnObject,hResultCode);
goto Failure;
}
pHostPlayer->Release();
pHostPlayer = NULL;
hResultCode = DPN_OK;
Exit:
CallbackThread.Deinitialize();
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pHostPlayer)
{
pHostPlayer->Release();
pHostPlayer = NULL;
}
if (pLocalPlayer)
{
pLocalPlayer->Release();
pLocalPlayer = NULL;
}
if (pLocalConnection)
{
pLocalConnection->Release();
pLocalConnection = NULL;
}
if (pIDevice)
{
IDirectPlay8Address_Release(pIDevice);
pIDevice = NULL;
}
if (pIHost)
{
IDirectPlay8Address_Release(pIHost);
pIHost = NULL;
}
if (pListenParent)
{
pListenParent->Release();
pListenParent = NULL;
}
if (pConnectParent)
{
pConnectParent->Release();
pConnectParent = NULL;
}
goto Exit;
}
// DNConnectToHostFailed
//
// Clean up if an attempt to connect to the HostPlayer fails
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToHostFailed"
HRESULT DNConnectToHostFailed(DIRECTNETOBJECT *const pdnObject,
PVOID const pvBuffer,
const DWORD dwBufferSize,
CConnection *const pConnection)
{
CAsyncOp *pConnectParent;
HRESULT hResultCode;
CRefCountBuffer *pRefCountBuffer;
UNALIGNED DN_INTERNAL_MESSAGE_CONNECT_FAILED *pInfo;
DPFX(DPFPREP, 4,"Parameters: pvBuffer [0x%p], dwBufferSize [%ld], pConnection [0x%x]",pvBuffer,dwBufferSize,pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pvBuffer != NULL || dwBufferSize == 0);
pRefCountBuffer = NULL;
pConnectParent = NULL;
if (pvBuffer != NULL)
{
pInfo = static_cast<DN_INTERNAL_MESSAGE_CONNECT_FAILED*>(pvBuffer);
if ((pInfo->dwReplyOffset != 0) && (pInfo->dwReplySize != 0))
{
//
// Extract reply buffer
//
if ((hResultCode = RefCountBufferNew(pdnObject,pInfo->dwReplySize,MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not create RefCountBuffer - ignore and continue");
DisplayDNError(0,hResultCode);
}
else
{
memcpy( pRefCountBuffer->GetBufferAddress(),
static_cast<BYTE*>(pvBuffer) + pInfo->dwReplyOffset,
pInfo->dwReplySize );
}
}
//
// Update connect operation parent with results
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
DNASSERT(pdnObject->pConnectParent);
if (pdnObject->pConnectParent)
{
pdnObject->pConnectParent->Lock();
pdnObject->pConnectParent->SetResult( pInfo->hResultCode );
pdnObject->pConnectParent->SetRefCountBuffer( pRefCountBuffer );
pdnObject->pConnectParent->Unlock();
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
DPFX(DPFPREP, 0,"ConnectToHostFailed: [0x%lx]",pInfo->hResultCode);
}
//
// Release the reference on the connection since we will be dropping the link.
// We are safe releasing the connection here, since the protocol will prevent
// the Host's DISCONNECT from being passed up to us until after this thread
// has returned back down.
//
pConnection->Disconnect();
//
// Clean up DirectNetObject
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
pdnObject->dwFlags &= (~(DN_OBJECT_FLAG_CONNECTED
| DN_OBJECT_FLAG_CONNECTING
| DN_OBJECT_FLAG_HOST_CONNECTED));
if (pdnObject->pConnectParent)
{
pConnectParent = pdnObject->pConnectParent;
pdnObject->pConnectParent = NULL;
}
if( pdnObject->pIDP8ADevice )
{
IDirectPlay8Address_Release( pdnObject->pIDP8ADevice );
pdnObject->pIDP8ADevice = NULL;
}
if( pdnObject->pConnectAddress )
{
IDirectPlay8Address_Release( pdnObject->pConnectAddress );
pdnObject->pConnectAddress = NULL;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
if (pConnectParent)
{
pConnectParent->Release();
pConnectParent = NULL;
}
DPFX(DPFPREP, 4,"Returning: DPN_OK");
return(DPN_OK);
}
//
// DNAbortConnect
//
// Abort the CONNECT process by cleaning up the DirectNet object and terminating the session
//
#undef DPF_MODNAME
#define DPF_MODNAME "DNAbortConnect"
HRESULT DNAbortConnect(DIRECTNETOBJECT *const pdnObject,
const HRESULT hrConnect)
{
HRESULT hResultCode;
CAsyncOp *pConnectParent;
DPFX(DPFPREP, 4,"Parameters: hrConnect [0x%lx]",hrConnect);
DNASSERT(pdnObject != NULL);
pConnectParent = NULL;
//
// Clean up DirectNetObject
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
pdnObject->dwFlags &= (~(DN_OBJECT_FLAG_CONNECTED
| DN_OBJECT_FLAG_CONNECTING
| DN_OBJECT_FLAG_HOST_CONNECTED));
if (pdnObject->pConnectParent)
{
pdnObject->pConnectParent->AddRef();
pConnectParent = pdnObject->pConnectParent;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
//
// Set error code on connect operation completion
//
if (pConnectParent)
{
pConnectParent->Lock();
pConnectParent->SetResult( hrConnect );
pConnectParent->Unlock();
}
//
// Shut down
//
DNTerminateSession(pdnObject,DPN_OK);
if (pConnectParent)
{
pConnectParent->Release();
pConnectParent = NULL;
}
hResultCode = DPN_OK;
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
}
// Player to player connection occurs at the direction of the host. (Peer-to-peer)
//
// Once a NewPlayer has connected to the host,
// The Host will send the NewPlayer's NameTable entry to all existing players
// Once the NewPlayer has installed the NameTable, it informs the host
// The Host will instruct the existing players to connect to the NewPlayer
//
// When an existing player establishes a connection with the NewPlayer
// The existing player send their DNID to the NewPlayer and an ADD_PLAYER to app
// The NewPlayer activates the connecting (existing) player and sends ADD_PLAYER to app
//
// DNPlayerConnect1
//
// Receive an existing player's DNID. This is called by the NewPlayer. If valid, send an
// ADD_PLAYER message to the application
#undef DPF_MODNAME
#define DPF_MODNAME "DNPlayerConnect1"
HRESULT DNPlayerConnect1(DIRECTNETOBJECT *const pdnObject,
const PVOID pv,
CConnection *const pConnection)
{
HRESULT hResultCode;
CNameTableEntry *pPlayer;
UNALIGNED DN_INTERNAL_MESSAGE_SEND_PLAYER_DPNID *pSend;
DPFX(DPFPREP, 4,"Parameters: pv [0x%p], pConnection [0x%p]",pv,pConnection);
DNASSERT(pdnObject != NULL);
DNASSERT(pv != NULL);
DNASSERT(pConnection != NULL);
pPlayer = NULL;
pSend = static_cast<DN_INTERNAL_MESSAGE_SEND_PLAYER_DPNID*>(pv);
DNASSERT(pSend->dpnid != 0);
DPFX(DPFPREP, 5,"Player [0x%lx] has connected",pSend->dpnid);
if ((hResultCode = pdnObject->NameTable.FindEntry(pSend->dpnid,&pPlayer)) != DPN_OK)
{
DPFERR("Could not find connecting player!");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Increment players in session
//
pdnObject->ApplicationDesc.IncPlayerCount( FALSE );
// Associate DNID with player's connection
pConnection->Lock();
pConnection->SetDPNID(pSend->dpnid);
pConnection->SetStatus( CONNECTED );
pConnection->Unlock();
// Populate connection
pdnObject->NameTable.PopulateConnection(pConnection);
//
// Now that this connection is part of a NameTableEntry, remove it from the indicated list
//
DNEnterCriticalSection(&pdnObject->csConnectionList);
if (!pConnection->m_bilinkIndicated.IsEmpty())
{
pConnection->Release();
}
pConnection->m_bilinkIndicated.RemoveFromList();
DNLeaveCriticalSection(&pdnObject->csConnectionList);
pPlayer->Release();
pPlayer = NULL;
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pPlayer)
{
pPlayer->Release();
pPlayer = NULL;
}
goto Exit;
}
// DNConnectToPeer1
//
// Accept the NameTable entry of the NewPlayer from the Host, and add it to the local
// NameTable. The Host will later provide an INSTRUCT_CONNECT message later.
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToPeer1"
HRESULT DNConnectToPeer1(DIRECTNETOBJECT *const pdnObject,
PVOID const pv)
{
HRESULT hResultCode;
DN_NAMETABLE_ENTRY_INFO *pdnNTEInfo;
CNameTableEntry *pNTEntry;
// DWORD dwVersion;
DPFX(DPFPREP, 4,"Parameters: pv [0x%p]",pv);
DNASSERT(pdnObject != NULL);
DNASSERT(pv != NULL);
pNTEntry = NULL;
//
// Create and unpack new entry
//
if ((hResultCode = NameTableEntryNew(pdnObject,&pNTEntry)) != DPN_OK)
{
DPFERR("Could not create new NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pdnNTEInfo = static_cast<DN_NAMETABLE_ENTRY_INFO*>(pv);
DPFX(DPFPREP, 5,"New player DNID [0x%lx] - installing NameTable entry",pdnNTEInfo->dpnid);
DPFX(DPFPREP, 5,"Connecting Player URL [%hs]",static_cast<char*>(pv) + pdnNTEInfo->dwURLOffset);
if ((hResultCode = pNTEntry->UnpackEntryInfo(pdnNTEInfo,static_cast<BYTE*>(pv))) != DPN_OK)
{
DPFERR("Could not unpack NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pNTEntry->StartConnecting();
//
// Add entry to NameTable
//
if ((hResultCode = pdnObject->NameTable.InsertEntry(pNTEntry)) != DPN_OK)
{
DPFERR("Could not insert NameTableEntry into NameTable");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
//
// Update NameTableVersion
//
pdnObject->NameTable.WriteLock();
pdnObject->NameTable.SetVersion( pdnNTEInfo->dwVersion );
pdnObject->NameTable.Unlock();
pNTEntry->Release();
pNTEntry = NULL;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
goto Exit;
}
// DNConnectToPeer2
//
// Perform a connection to the NewPlayer (as instructed by Host).
// Once this connection has completed (DNConnectToPeer2) send this player's DNID
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToPeer2"
HRESULT DNConnectToPeer2(DIRECTNETOBJECT *const pdnObject,
PVOID const pv)
{
HRESULT hResultCode;
CNameTableEntry *pNTEntry;
CNameTableEntry *pLocalPlayer;
UNALIGNED DN_INTERNAL_MESSAGE_INSTRUCT_CONNECT *pInfo;
CServiceProvider *pSP;
DPFX(DPFPREP, 4,"Parameters: pv [0x%p]", pv);
DNASSERT(pdnObject != NULL);
DNASSERT(pv != NULL);
pNTEntry = NULL;
pLocalPlayer = NULL;
pSP = NULL;
pInfo = static_cast<DN_INTERNAL_MESSAGE_INSTRUCT_CONNECT*>(pv);
//
// Update NameTable version
//
pdnObject->NameTable.WriteLock();
pdnObject->NameTable.SetVersion( pInfo->dwVersion );
pdnObject->NameTable.Unlock();
//
// Determine if this is an instruction to connect to ourselves.
// If it is, don't do anything other than update the NameTable version
//
DPFX(DPFPREP, 5,"Instructed to connect to [0x%lx]",pInfo->dpnid);
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
{
DPFERR("Could not get local player reference");
DisplayDNError(0,hResultCode);
goto Failure;
}
if (pLocalPlayer->GetDPNID() == pInfo->dpnid)
{
DPFX(DPFPREP, 5,"Ignoring instruction to connect to self");
hResultCode = DPN_OK;
goto Failure;
}
if ((hResultCode = pdnObject->NameTable.FindEntry(pInfo->dpnid,&pNTEntry)) != DPN_OK)
{
DPFERR("Could not find NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
//
// We will not connect to older players. They will connect to us.
//
if (pNTEntry->GetVersion() < pLocalPlayer->GetVersion())
{
DPFX(DPFPREP, 5,"Ignoring instruction to connect to older player");
hResultCode = DPN_OK;
goto Failure;
}
pLocalPlayer->Release();
pLocalPlayer = NULL;
//
// Get SP (cached on DirectNet object from original Connect() to host)
//
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->pConnectSP != NULL)
{
pdnObject->pConnectSP->AddRef();
pSP = pdnObject->pConnectSP;
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
if (pSP == NULL)
{
DPFERR("Could not find connect SP on DirectNet object");
hResultCode = DPNERR_GENERIC;
goto Failure;
}
#ifndef DPNBUILD_ONLYONESP
//
// Force the correct service provider into the address.
//
GUID guidSP;
pSP->GetGUID(&guidSP);
if ((hResultCode = IDirectPlay8Address_SetSP(pNTEntry->GetAddress(), &guidSP)) != DPN_OK)
{
DPFERR("Could not force correct service provider into player address");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
#endif // ! DPNBUILD_ONLYONESP
// Connect to new player
DPFX(DPFPREP, 5,"Performing Connect");
DNASSERT(pdnObject->pIDP8ADevice != NULL);
hResultCode = DNPerformConnect( pdnObject,
pNTEntry->GetDPNID(),
pdnObject->pIDP8ADevice,
pNTEntry->GetAddress(),
pSP,
((pdnObject->ApplicationDesc.GetReservedDataSize() > 0) ? DN_CONNECTFLAGS_SESSIONDATA : 0),
NULL);
if (hResultCode == DPNERR_PENDING)
{
hResultCode = DPN_OK;
}
pSP->Release();
pSP = NULL;
pNTEntry->Release();
pNTEntry = NULL;
Exit:
DNASSERT( pSP == NULL );
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
if (pLocalPlayer)
{
pLocalPlayer->Release();
pLocalPlayer = NULL;
}
if (pSP)
{
pSP->Release();
pSP = NULL;
}
goto Exit;
}
// DNConnectToPeer3
//
// Finish the connection process to the NewPlayer.
// Once the connection has completed send this player's DNID to NewPlayer and propegate
// ADD_PLAYER message
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToPeer3"
HRESULT DNConnectToPeer3(DIRECTNETOBJECT *const pdnObject,
const DPNID dpnid,
CConnection *const pConnection)
{
HRESULT hResultCode;
CNameTableEntry *pNTEntry;
CNameTableEntry *pLocalPlayer;
CRefCountBuffer *pRefCountBuffer;
DN_INTERNAL_MESSAGE_SEND_PLAYER_DPNID *pSend;
DPFX(DPFPREP, 4,"Parameters: dpnid [0x%lx], pConnection [0x%p]",dpnid,pConnection);
DNASSERT(dpnid != NULL);
DNASSERT(pConnection != NULL);
pLocalPlayer = NULL;
pNTEntry = NULL;
pRefCountBuffer = NULL;
//
// increment players in session
//
pdnObject->ApplicationDesc.IncPlayerCount( FALSE );
// Associate DNID with player's end point handle
pConnection->Lock();
pConnection->SetDPNID(dpnid);
pConnection->SetStatus( CONNECTED );
pConnection->Unlock();
//
// Get New Players's entry
//
if ((hResultCode = pdnObject->NameTable.FindEntry(dpnid,&pNTEntry)) != DPN_OK)
{
DPFERR("Could not find new player");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Get Local player's entry
//
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
{
DPFERR("Could not get local player reference");
DisplayDNError(0,hResultCode);
goto Failure;
}
// Setup buffer to pass local player's DNID
hResultCode = RefCountBufferNew(pdnObject,
sizeof(DN_INTERNAL_MESSAGE_SEND_PLAYER_DPNID),
MemoryBlockAlloc,
MemoryBlockFree,
&pRefCountBuffer);
if (hResultCode != DPN_OK)
{
DPFERR("Could not allocate buffer to send connecting player DPNID");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pSend = reinterpret_cast<DN_INTERNAL_MESSAGE_SEND_PLAYER_DPNID*>(pRefCountBuffer->GetBufferAddress());
pSend->dpnid = pLocalPlayer->GetDPNID();
pLocalPlayer->Release();
pLocalPlayer = NULL;
// Send player's DNID to new player
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_SEND_PLAYER_DNID,
dpnid,
pRefCountBuffer->BufferDescAddress(),
1,
pRefCountBuffer,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
if (hResultCode != DPNERR_PENDING)
{
DPFERR("Could not send connecting player DPNID");
DisplayDNError(0,hResultCode);
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
DNASSERT(FALSE);
goto Failure;
}
// Update NameTableEntry with Connection
pdnObject->NameTable.PopulateConnection(pConnection);
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
pNTEntry->Release();
pNTEntry = NULL;
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pNTEntry)
{
pNTEntry->Release();
pNTEntry = NULL;
}
if (pLocalPlayer)
{
pLocalPlayer->Release();
pLocalPlayer = NULL;
}
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
goto Exit;
}
// DNConnectToPeerFailed
//
// An existing player could not connect to a NewPlayer.
// Send a message to the Host player that this connect attempt failed
#undef DPF_MODNAME
#define DPF_MODNAME "DNConnectToPeerFailed"
HRESULT DNConnectToPeerFailed(DIRECTNETOBJECT *const pdnObject,
const DPNID dpnid)
{
HRESULT hResultCode;
CNameTableEntry *pHost;
CConnection *pConnection;
CRefCountBuffer *pRCBuffer;
DN_INTERNAL_MESSAGE_INSTRUCTED_CONNECT_FAILED *pMsg;
DPFX(DPFPREP, 4,"Parameters: dpnid [0x%lx]",dpnid);
DNASSERT(pdnObject != NULL);
DNASSERT(dpnid != 0);
pHost = NULL;
pConnection = NULL;
pRCBuffer = NULL;
if ((hResultCode = pdnObject->NameTable.GetHostPlayerRef( &pHost )) != DPN_OK)
{
DPFERR("Could not get Host player reference");
DisplayDNError(0,hResultCode);
goto Failure;
}
if ((hResultCode = pHost->GetConnectionRef( &pConnection )) != DPN_OK)
{
DPFERR("Could not get Host Connection reference");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Create message
//
hResultCode = RefCountBufferNew(pdnObject,
sizeof(DN_INTERNAL_MESSAGE_INSTRUCTED_CONNECT_FAILED),
MemoryBlockAlloc,
MemoryBlockFree,
&pRCBuffer);
if (hResultCode != DPN_OK)
{
DPFERR("Could not create RefCountBuffer");
DisplayDNError(0,hResultCode);
goto Failure;
}
pMsg = reinterpret_cast<DN_INTERNAL_MESSAGE_INSTRUCTED_CONNECT_FAILED*>(pRCBuffer->GetBufferAddress());
pMsg->dpnid = dpnid;
//
// Send message
//
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_INSTRUCTED_CONNECT_FAILED,
pHost->GetDPNID(),
pRCBuffer->BufferDescAddress(),
1,
pRCBuffer,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
if (hResultCode != DPNERR_PENDING)
{
DPFERR("Could not send CONNECT_FAILED message - maybe OUR connection is down !");
DisplayDNError(0,hResultCode);
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
goto Failure;
}
//
// Clean up
//
pHost->Release();
pHost = NULL;
pConnection->Release();
pConnection = NULL;
pRCBuffer->Release();
pRCBuffer = NULL;
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 4,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pHost)
{
pHost->Release();
pHost = NULL;
}
if (pConnection)
{
pConnection->Release();
pConnection = NULL;
}
if (pRCBuffer)
{
pRCBuffer->Release();
pRCBuffer = NULL;
}
goto Exit;
}
// DNSendConnectInfo
//
// Send connection info to a new player.
// This is the Host Applcation Description and the NameTable
// This uses an enumeration buffer.
// The format of the buffer is:
// <DN_INTERNAL_MESSAGE_CONNECT_INFO>
// <DN_APPLICATION_DESC>
// <DN_NAMETABLE_INFO>
// <DN_NAMETABLE_ENTRY_INFO>
// <DN_NAMETABLE_ENTRY_INFO>
// :
// <strings and data blocks>
//
// DNID dnId DNID of new player
#undef DPF_MODNAME
#define DPF_MODNAME "DNSendConnectInfo"
HRESULT DNSendConnectInfo(DIRECTNETOBJECT *const pdnObject,
CNameTableEntry *const pNTEntry,
CConnection *const pConnection,
void *const pvReplyBuffer,
const DWORD dwReplyBufferSize)
{
HRESULT hResultCode;
CPackedBuffer packedBuffer;
CRefCountBuffer *pRefCountBuffer;
DN_INTERNAL_MESSAGE_CONNECT_INFO *pMsg;
DPFX(DPFPREP, 6,"Parameters: pNTEntry [0x%p], pConnection [0x%p], pvReplyBuffer [0x%p], dwReplyBufferSize [%ld]",
pNTEntry,pConnection,pvReplyBuffer,dwReplyBufferSize);
DNASSERT(pdnObject != NULL);
DNASSERT(pNTEntry != NULL);
pRefCountBuffer = NULL;
//
// Determine size of message
//
packedBuffer.Initialize(NULL,0);
packedBuffer.AddToFront(NULL,sizeof(DN_INTERNAL_MESSAGE_CONNECT_INFO));
packedBuffer.AddToBack(NULL,dwReplyBufferSize);
pdnObject->ApplicationDesc.PackInfo(&packedBuffer,
DN_APPDESCINFO_FLAG_SESSIONNAME|DN_APPDESCINFO_FLAG_PASSWORD|DN_APPDESCINFO_FLAG_RESERVEDDATA|
DN_APPDESCINFO_FLAG_APPRESERVEDDATA);
pdnObject->NameTable.PackNameTable(pNTEntry,&packedBuffer);
//
// Create buffer
//
if ((hResultCode = RefCountBufferNew(pdnObject,packedBuffer.GetSizeRequired(),MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not allocate RefCountBuffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
packedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(),pRefCountBuffer->GetBufferSize());
//
// Fill in buffer
//
pMsg = static_cast<DN_INTERNAL_MESSAGE_CONNECT_INFO*>(packedBuffer.GetHeadAddress());
if ((hResultCode = packedBuffer.AddToFront(NULL,sizeof(DN_INTERNAL_MESSAGE_CONNECT_INFO))) != DPN_OK)
{
DPFERR("Could not add CONNECT_INFO struct to packed buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
if ((pvReplyBuffer) && (dwReplyBufferSize != 0))
{
if ((hResultCode = packedBuffer.AddToBack(pvReplyBuffer,dwReplyBufferSize)) != DPN_OK)
{
DPFERR("Could not add reply buffer to packed buffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pMsg->dwReplyOffset = packedBuffer.GetTailOffset();
pMsg->dwReplySize = dwReplyBufferSize;
}
else
{
pMsg->dwReplyOffset = 0;
pMsg->dwReplySize = 0;
}
hResultCode = pdnObject->ApplicationDesc.PackInfo(&packedBuffer,
DN_APPDESCINFO_FLAG_SESSIONNAME|DN_APPDESCINFO_FLAG_PASSWORD|DN_APPDESCINFO_FLAG_RESERVEDDATA|
DN_APPDESCINFO_FLAG_APPRESERVEDDATA);
if (hResultCode != DPN_OK)
{
DPFERR("Could not pack ApplicationDesc");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
if ((hResultCode = pdnObject->NameTable.PackNameTable(pNTEntry,&packedBuffer)) != DPN_OK)
{
DPFERR("Could not pack NameTable");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
// Send the name table to target
hResultCode = DNSendMessage(pdnObject,
pConnection,
DN_MSG_INTERNAL_SEND_CONNECT_INFO,
pNTEntry->GetDPNID(),
pRefCountBuffer->BufferDescAddress(),
1,
pRefCountBuffer,
0,
DN_SENDFLAGS_RELIABLE,
NULL,
NULL);
if (hResultCode != DPNERR_PENDING)
{
DPFERR("Could not send connect info to joining player");
DisplayDNError(0,hResultCode);
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
// DNASSERT(FALSE);
goto Failure;
}
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
goto Exit;
}
// DNReceiveConnectInfo
//
// Receive connect info from the host/server player.
// This is the Application Description and the NameTable
// The name table is in an enum buffer, with relative pointer references which will have
// to be turned into absolutes. This process requires two passes of the buffer.
// The first pass will extract PLAYERS, and the second pass will extract groups.
//
// PVOID pvNTBuffer Pointer to name table enum buffer
// DWORD dwNumEntries Number of entries in the name table
#undef DPF_MODNAME
#define DPF_MODNAME "DNReceiveConnectInfo"
HRESULT DNReceiveConnectInfo(DIRECTNETOBJECT *const pdnObject,
void *const pvBuffer,
CConnection *const pHostConnection,
DPNID *const pdpnid)
{
HRESULT hResultCode;
void *pvReplyBuffer;
DWORD dwReplyBufferSize;
UNALIGNED DPN_APPLICATION_DESC_INFO *pdnAppDescInfo;
UNALIGNED DN_NAMETABLE_INFO *pdnNTInfo;
CRefCountBuffer *pRefCountBuffer;
CAsyncOp *pConnect;
UNALIGNED DN_INTERNAL_MESSAGE_CONNECT_INFO *pMsg;
DPFX(DPFPREP, 6,"Parameters: pvBuffer [0x%p], pHostConnection [0x%p], pdpnid [0x%p]",
pvBuffer,pHostConnection,pdpnid);
DNASSERT(pdnObject != NULL);
DNASSERT(pdpnid != NULL);
DNASSERT(pvBuffer != NULL);
DNASSERT(pHostConnection != NULL);
pRefCountBuffer = NULL;
pConnect = NULL;
//
// Pull fixed structures from front of buffer
//
pMsg = static_cast<DN_INTERNAL_MESSAGE_CONNECT_INFO*>(pvBuffer);
pdnAppDescInfo = reinterpret_cast<DPN_APPLICATION_DESC_INFO*>(pMsg + 1);
pdnNTInfo = reinterpret_cast<DN_NAMETABLE_INFO*>(pdnAppDescInfo + 1);
//
// Extract reply buffer
//
dwReplyBufferSize = pMsg->dwReplySize;
if (dwReplyBufferSize > 0)
{
pvReplyBuffer = static_cast<void*>(static_cast<BYTE*>(pvBuffer) + pMsg->dwReplyOffset);
DPFX(DPFPREP, 7,"Reply Buffer [0x%p] [%ld]",pvReplyBuffer,dwReplyBufferSize);
if ((hResultCode = RefCountBufferNew(pdnObject,dwReplyBufferSize,MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
{
DPFERR("Could not create RefCountBuffer");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
memcpy(pRefCountBuffer->GetBufferAddress(),pvReplyBuffer,dwReplyBufferSize);
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
if (pdnObject->pConnectParent)
{
pdnObject->pConnectParent->AddRef();
pConnect = pdnObject->pConnectParent;
pConnect->SetRefCountBuffer( pRefCountBuffer );
}
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
//
// Extract Application Description
//
DPFX(DPFPREP, 7,"Extracting Application Description");
hResultCode = pdnObject->ApplicationDesc.UnpackInfo(pdnAppDescInfo,pvBuffer,DN_APPDESCINFO_FLAG_SESSIONNAME |
DN_APPDESCINFO_FLAG_PASSWORD | DN_APPDESCINFO_FLAG_RESERVEDDATA | DN_APPDESCINFO_FLAG_APPRESERVEDDATA);
if (hResultCode != DPN_OK)
{
DPFERR("Could not unpack ApplicationDesc");
DisplayDNError(0,hResultCode);
goto Failure;
}
//
// Set player count (Host and LocalPlayer)
//
#pragma BUGBUG( minara,"What should happen here ?" )
// dnAppDesc.dwCurrentPlayers = 2;
//
// Extract NameTable
//
DPFX(DPFPREP, 7,"Extracting NameTable");
DPFX(DPFPREP, 7,"Set DPNID mask to [0x%lx]",pdnObject->ApplicationDesc.GetDPNIDMask());
pdnObject->NameTable.SetDPNIDMask( pdnObject->ApplicationDesc.GetDPNIDMask() );
if ((hResultCode = pdnObject->NameTable.UnpackNameTableInfo(pdnNTInfo,static_cast<BYTE*>(pvBuffer),pdpnid)) != DPN_OK)
{
DPFERR("Could not unpack NameTable");
DisplayDNError(0,hResultCode);
goto Failure;
}
if (pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
{
//
// Create ALL_PLAYERS group
//
CNameTableEntry *pAllPlayersGroup;
pAllPlayersGroup = NULL;
if ((hResultCode = NameTableEntryNew(pdnObject,&pAllPlayersGroup)) != DPN_OK)
{
DPFERR("Could not create NameTableEntry");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Failure;
}
pAllPlayersGroup->MakeGroup();
// This function takes the lock internally
pdnObject->NameTable.MakeAllPlayersGroup(pAllPlayersGroup);
pAllPlayersGroup->Release();
pAllPlayersGroup = NULL;
DNASSERT(pAllPlayersGroup == NULL);
}
if (pConnect)
{
pConnect->Release();
pConnect = NULL;
}
Exit:
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
Failure:
if (pRefCountBuffer)
{
pRefCountBuffer->Release();
pRefCountBuffer = NULL;
}
if (pConnect)
{
pConnect->SetResult( hResultCode ); // Try and salvage something !
pConnect->Release();
pConnect = NULL;
}
goto Exit;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNGetClearAddress"
HRESULT DNGetClearAddress(DIRECTNETOBJECT *const pdnObject,
const HANDLE hEndPt,
IDirectPlay8Address **const ppAddress,
const BOOL fPartner)
{
SPGETADDRESSINFODATA spInfoData;
HRESULT hResultCode;
#ifdef DBG
TCHAR DP8ABuffer[512] = {0};
DWORD DP8ASize;
#endif // DBG
DPFX(DPFPREP, 6,"Parameters: hEndPt [0x%p], ppAddress [0x%p], fPartner [%ld]",hEndPt,ppAddress,fPartner);
DNASSERT(pdnObject != NULL);
DNASSERT(hEndPt != NULL);
DNASSERT(ppAddress != NULL);
if (fPartner)
{
spInfoData.Flags = SP_GET_ADDRESS_INFO_REMOTE_HOST;
}
else
{
spInfoData.Flags = SP_GET_ADDRESS_INFO_LOCAL_HOST_PUBLIC_ADDRESS;
}
hResultCode = DNPCrackEndPointDescriptor(pdnObject->pdnProtocolData,hEndPt,&spInfoData);
if (hResultCode != DPN_OK)
{
DPFERR("Unknown error from DNPCrackEndPointDescriptor");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Exit;
}
*ppAddress = spInfoData.pAddress;
spInfoData.pAddress = NULL;
#ifdef DBG
if (*ppAddress)
{
DP8ASize = 512;
IDirectPlay8Address_GetURL(*ppAddress,DP8ABuffer,&DP8ASize);
DPFX(DPFPREP, 5,"Remote Address [%s]",DP8ABuffer);
}
#endif // DBG
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNGetLocalDeviceAddress"
HRESULT DNGetLocalDeviceAddress(DIRECTNETOBJECT *const pdnObject,
const HANDLE hEndPt,
IDirectPlay8Address **const ppAddress)
{
SPGETADDRESSINFODATA spInfoData;
HRESULT hResultCode;
#ifdef DBG
TCHAR DP8ABuffer[512] = {0};
DWORD DP8ASize;
#endif // DBG
DPFX(DPFPREP, 6,"Parameters: hEndPt [0x%p], ppAddress [0x%p]",hEndPt,ppAddress);
DNASSERT(pdnObject != NULL);
DNASSERT(hEndPt != NULL);
DNASSERT(ppAddress != NULL);
spInfoData.Flags = SP_GET_ADDRESS_INFO_LOCAL_ADAPTER;
hResultCode = DNPCrackEndPointDescriptor(pdnObject->pdnProtocolData,hEndPt,&spInfoData);
if (hResultCode != DPN_OK)
{
DPFERR("Unknown error from DNPCrackEndPointDescriptor");
DisplayDNError(0,hResultCode);
DNASSERT(FALSE);
goto Exit;
}
*ppAddress = spInfoData.pAddress;
spInfoData.pAddress = NULL;
#ifdef DBG
if (*ppAddress)
{
DP8ASize = 512;
IDirectPlay8Address_GetURL(*ppAddress,DP8ABuffer,&DP8ASize);
DPFX(DPFPREP, 7,"Remote Address [%s]",DP8ABuffer);
}
#endif // DBG
hResultCode = DPN_OK;
Exit:
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
return(hResultCode);
}