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.
1835 lines
52 KiB
1835 lines
52 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: Migration.cpp
|
|
* Content: DNET Host Migration Routines
|
|
*@@BEGIN_MSINTERNAL
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 12/23/99 mjn Created
|
|
* 12/23/99 mjn Fixed basic host migration
|
|
* 12/28/99 mjn Added DNCompleteOutstandingOperations
|
|
* 12/28/99 mjn Added NameTable version to Host migration message
|
|
* 12/28/99 mjn Moved Async Op stuff to Async.h
|
|
* 01/04/00 mjn Added code to allow outstanding ops to complete at host migration
|
|
* 01/06/00 mjn Moved NameTable stuff to NameTable.h
|
|
* 01/11/00 mjn Moved connect/disconnect stuff to Connect.h
|
|
* 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 Generate DN_MSGID_HOST_MIGRATE at host migration
|
|
* 01/19/00 mjn Auto destruct groups at host migration
|
|
* 01/24/00 mjn Use DNNTUpdateVersion to update NameTable version
|
|
* 01/25/00 mjn Send dwLatestVersion to Host at migration
|
|
* 01/25/00 mjn Changed Host Migration to multi-step affair
|
|
* 01/26/00 mjn Implemented NameTable re-sync at host migration
|
|
* 02/01/00 mjn Implement Player/Group context values
|
|
* 02/04/00 mjn Clean up NameTable to remove old entries
|
|
* 03/25/00 rmt Added calls into DPNSVR modules
|
|
* 04/04/00 rmt Added check for DPNSVR disable flag before attempting to register w/DPNSVR
|
|
* 04/16/00 mjn DNSendMessage() used CAsyncOp
|
|
* 04/18/00 mjn CConnection tracks connection status better
|
|
* mjn Fixed player count problem
|
|
* 04/19/00 mjn Update NameTable operations to use DN_WORKER_JOB_SEND_NAMETABLE_OPERATION
|
|
* 04/25/00 mjn Fixed migration process to ensure use of CAsyncOp
|
|
* mjn Migration calls CoInitialize()/CoUninitialize() before registering w/ DPNSVR
|
|
* 05/03/00 mjn Use GetHostPlayerRef() rather than GetHostPlayer()
|
|
* 05/04/00 mjn Ensure local player still in session for Host migration
|
|
* 05/16/00 mjn Do not take locks when clearing NameTable short-cut pointers
|
|
* 06/25/00 mjn Ignore players older than old Host when determining new Host in DNFindNewHost()
|
|
* 07/06/00 mjn Fixed locking problem in CNameTable::MakeLocalPlayer,MakeHostPlayer,MakeAllPlayersGroup
|
|
* 07/07/00 mjn Added shut down checks to migration code.
|
|
* 07/20/00 mjn Cleaned up leaking RefCountBuffers and added closing tests
|
|
* 07/21/00 mjn Added code in DNCheckReceivedAllVersions() to skip disconnecting players
|
|
* 07/27/00 rmt Bug #40882 - DPLSESSION_HOSTMIGRATED status update is not sent
|
|
* 07/31/00 mjn Added dwDestroyReason to DNHostDisconnect()
|
|
* 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
|
|
* 08/05/00 mjn Added pParent to DNSendGroupMessage and DNSendMessage()
|
|
* mjn Added fInternal to DNPerformChildSend()
|
|
* 08/06/00 mjn Added CWorkerJob
|
|
* 08/07/00 mjn Handle integrity check requests during host migration
|
|
* 08/08/00 mjn Moved DN_NAMETABLE_OP_INFO to Message.h
|
|
* 08/11/00 mjn Added DN_OBJECT_FLAG_HOST_MIGRATING_2 to prevent multiple threads from passing DNCheckReceivedAllVersions()
|
|
* 08/15/00 mjn Perform LISTEN registration with DPNSVR in PerformHostMigration3()
|
|
* 08/24/00 mjn Replace DN_NAMETABLE_OP with CNameTableOp
|
|
* 09/04/00 mjn Added CApplicationDesc
|
|
* 09/06/00 mjn Fixed register with DPNSVR problem
|
|
* 09/17/00 mjn Split CNameTable.m_bilinkEntries into m_bilinkPlayers and m_bilinkGroups
|
|
* 10/12/00 mjn Ensure proper locks taken when traversing CNameTable::m_bilinkEntries
|
|
* 01/25/01 mjn Fixed 64-bit alignment problem in received messages
|
|
* 03/30/01 mjn Changes to prevent multiple loading/unloading of SP's
|
|
* 04/05/01 mjn Added DPNID parameter to DNProcessHostMigration3() to ensure correct new host is completing host migration
|
|
* 04/13/01 mjn Use request list instead of async op list when retrying outstanding requests
|
|
* 05/17/01 mjn Wait for threads performing NameTable operations to finish before sending NameTable version during host migration
|
|
* 07/22/01 mjn Added DPNBUILD_NOHOSTMIGRATE compile flag
|
|
*@@END_MSINTERNAL
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "dncorei.h"
|
|
|
|
#ifndef DPNBUILD_NOHOSTMIGRATE
|
|
|
|
|
|
// DNFindNewHost
|
|
//
|
|
// Find the new host from the entries in the NameTable.
|
|
// This is based on version number of the players in the NameTable. The player with
|
|
// the oldest version number (after the old Host) will be the new Host.
|
|
//
|
|
// DPNID *pdpnidNewHost New Host DNID
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNFindNewHost"
|
|
|
|
HRESULT DNFindNewHost(DIRECTNETOBJECT *const pdnObject,
|
|
DPNID *const pdpnidNewHost)
|
|
{
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
CNameTableEntry *pLocalPlayer;
|
|
CNameTableEntry *pHostPlayer;
|
|
HRESULT hResultCode;
|
|
DWORD dwVersionNewHost;
|
|
DWORD dwVersionOldHost;
|
|
DPNID dpnidNewHost;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pdpnidNewHost [0x%p]",pdpnidNewHost);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pNTEntry = NULL;
|
|
pLocalPlayer = NULL;
|
|
pHostPlayer = NULL;
|
|
|
|
//
|
|
// Seed local player as new Host
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get local player reference");
|
|
DisplayDNError(0,hResultCode);
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
goto Failure;
|
|
}
|
|
dpnidNewHost = pLocalPlayer->GetDPNID();
|
|
dwVersionNewHost = pLocalPlayer->GetVersion();
|
|
DPFX(DPFPREP, 7,"First Host Candidate: dpnid [0x%lx], dwVersion [%ld]",dpnidNewHost,dwVersionNewHost);
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
|
|
//
|
|
// Determine old host player version
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.GetHostPlayerRef( &pHostPlayer )) == DPN_OK)
|
|
{
|
|
dwVersionOldHost = pHostPlayer->GetVersion();
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
}
|
|
else
|
|
{
|
|
dwVersionOldHost = 0;
|
|
}
|
|
|
|
//
|
|
// Lock down NameTable
|
|
//
|
|
pdnObject->NameTable.ReadLock();
|
|
|
|
// Traverse NameTable to find player with next older version
|
|
// TODO - we should release the NameTable CS so that leaving players get updated in NameTable
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && (pNTEntry->GetVersion() < dwVersionNewHost))
|
|
{
|
|
//
|
|
// There are some conditions where we might have players older than the Host in the NameTable
|
|
// Consider the following: P1,P2,P3,P4 (P1 is Host)
|
|
// - P1, P2 drop
|
|
// - P3 detects drop of P1 and P2
|
|
// - P4 detects drop of P1 only (P2 may not yet have timed out)
|
|
// - P3 sends HOST_MIGRATE message to P4
|
|
// - P4 makes P3 new Host, but P2 is still in NameTable (at this stage)
|
|
// - P3 drops (P2's drop sill has not been detected!)
|
|
// We should therefore ignore all players older than the old Host,
|
|
//
|
|
if (pNTEntry->GetVersion() < dwVersionOldHost)
|
|
{
|
|
DPFERR("Found player older than old Host ! (Have we missed a drop ?)");
|
|
}
|
|
else
|
|
{
|
|
dpnidNewHost = pNTEntry->GetDPNID();
|
|
dwVersionNewHost = pNTEntry->GetVersion();
|
|
DPFX(DPFPREP, 7,"Better Host Candidate: dpnid [0x%lx], dwVersion [%ld]",
|
|
dpnidNewHost,dwVersionNewHost);
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
pNTEntry = NULL;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
//
|
|
// Unlock NameTable
|
|
//
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
DPFX(DPFPREP, 7,"Found New Host: dpnid [0x%lx], dwVersion [%ld]",dpnidNewHost,dwVersionNewHost);
|
|
|
|
if (pdpnidNewHost)
|
|
*pdpnidNewHost = dpnidNewHost;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pLocalPlayer)
|
|
{
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
}
|
|
if (pHostPlayer)
|
|
{
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformHostMigration1
|
|
//
|
|
// Perform host migration to become the new Host.
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformHostMigration1"
|
|
|
|
HRESULT DNPerformHostMigration1(DIRECTNETOBJECT *const pdnObject,
|
|
const DPNID dpnidOldHost)
|
|
{
|
|
HRESULT hResultCode;
|
|
CRefCountBuffer *pRefCountBuffer;
|
|
CWorkerJob *pWorkerJob;
|
|
CNameTableEntry *pLocalPlayer;
|
|
CPendingDeletion *pPending;
|
|
DN_INTERNAL_MESSAGE_HOST_MIGRATE *pInfo;
|
|
CBilink *pBilink;
|
|
CNameTableOp *pNTOp;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnidOldHost [0x%lx]",dpnidOldHost);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pLocalPlayer = NULL;
|
|
pRefCountBuffer = NULL;
|
|
pPending = NULL;
|
|
pWorkerJob = NULL;
|
|
pNTOp = NULL;
|
|
|
|
//
|
|
// Need reference on local player
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get reference on LocalPlayer");
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Flag as performing host migration - ensure we're not already running this from another thread
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_HOST_MIGRATING;
|
|
DNASSERT(pdnObject->pNewHost == NULL);
|
|
pLocalPlayer->AddRef();
|
|
pdnObject->pNewHost = pLocalPlayer;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Put this on the Outstanding operation list
|
|
//
|
|
if ((hResultCode = PendingDeletionNew(pdnObject,&pPending)) == DPN_OK)
|
|
{
|
|
pPending->SetDPNID( dpnidOldHost );
|
|
|
|
DNEnterCriticalSection(&pdnObject->csNameTableOpList);
|
|
pPending->m_bilinkPendingDeletions.InsertBefore(&pdnObject->m_bilinkPendingDeletions);
|
|
DNLeaveCriticalSection(&pdnObject->csNameTableOpList);
|
|
|
|
pPending = NULL;
|
|
}
|
|
#ifndef DPNBUILD_NOLOBBY
|
|
DNUpdateLobbyStatus(pdnObject,DPLSESSION_HOSTMIGRATEDHERE);
|
|
#endif // ! DPNBUILD_NOLOBBY
|
|
|
|
//
|
|
// Make new host
|
|
//
|
|
pdnObject->NameTable.UpdateHostPlayer( pLocalPlayer );
|
|
|
|
//
|
|
// We will need to wait for all threads performing name table operations to finish running
|
|
// before we send the current name table version to the host player
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_HOST_MIGRATING_WAIT;
|
|
if (pdnObject->dwRunningOpCount > 0)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DPFX(DPFPREP,7,"Waiting for running threads to finish");
|
|
IDirectPlay8ThreadPoolWork_WaitWhileWorking(pdnObject->pIDPThreadPoolWork,
|
|
HANDLE_FROM_DNHANDLE(pdnObject->hRunningOpEvent),
|
|
0);
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP,7,"No running threads to wait for");
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Clean-up outstanding operations
|
|
//
|
|
DPFX(DPFPREP,7,"Cleaning up outstanding NameTable operations");
|
|
pdnObject->NameTable.WriteLock();
|
|
DPFX(DPFPREP,7,"NameTable version [%ld]",pdnObject->NameTable.GetVersion());
|
|
pBilink = pdnObject->NameTable.m_bilinkNameTableOps.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkNameTableOps)
|
|
{
|
|
pNTOp = CONTAINING_OBJECT(pBilink,CNameTableOp,m_bilinkNameTableOps);
|
|
pBilink = pBilink->GetNext();
|
|
if (pNTOp->GetVersion() > pdnObject->NameTable.GetVersion())
|
|
{
|
|
DPFX(DPFPREP,7,"Removing outstanding operation [0x%p], version [%ld]",pNTOp,pNTOp->GetVersion());
|
|
pNTOp->m_bilinkNameTableOps.RemoveFromList();
|
|
if (pNTOp->GetRefCountBuffer())
|
|
{
|
|
pNTOp->GetRefCountBuffer()->Release();
|
|
pNTOp->SetRefCountBuffer( NULL );
|
|
}
|
|
if (pNTOp->GetSP())
|
|
{
|
|
pNTOp->GetSP()->Release();
|
|
pNTOp->SetSP( NULL );
|
|
}
|
|
pNTOp->ReturnSelfToPool();
|
|
}
|
|
pNTOp = NULL;
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_HOST_MIGRATING_WAIT);
|
|
DNResetEvent(pdnObject->hRunningOpEvent);
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Update NameTable operation list version
|
|
//
|
|
hResultCode = DNNTPlayerSendVersion(pdnObject);
|
|
|
|
//
|
|
// Update protocol (and SP)
|
|
DNUpdateListens(pdnObject,DN_UPDATE_LISTEN_FLAG_HOST_MIGRATE);
|
|
|
|
// Only need to proceed if we're not the only player left in the session
|
|
if ((hResultCode = DNCheckReceivedAllVersions(pdnObject)) != DPN_OK)
|
|
{
|
|
// Inform other players of host migration
|
|
DPFX(DPFPREP, 7,"Informing other players of host migration");
|
|
hResultCode = RefCountBufferNew(pdnObject,
|
|
sizeof(DN_INTERNAL_MESSAGE_HOST_MIGRATE),
|
|
MemoryBlockAlloc,
|
|
MemoryBlockFree,
|
|
&pRefCountBuffer);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not create RefCountBuffer for Host Migration");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pInfo = reinterpret_cast<DN_INTERNAL_MESSAGE_HOST_MIGRATE*>(pRefCountBuffer->GetBufferAddress());
|
|
pInfo->dpnidNewHost = pLocalPlayer->GetDPNID();
|
|
pInfo->dpnidOldHost = dpnidOldHost;
|
|
|
|
if ((hResultCode = WorkerJobNew(pdnObject,&pWorkerJob)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create WorkerJob for Host Migration");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pWorkerJob->SetJobType( WORKER_JOB_SEND_NAMETABLE_OPERATION );
|
|
pWorkerJob->SetSendNameTableOperationMsgId( DN_MSG_INTERNAL_HOST_MIGRATE );
|
|
pWorkerJob->SetSendNameTableOperationVersion( 0 );
|
|
pWorkerJob->SetSendNameTableOperationDPNIDExclude( 0 );
|
|
pWorkerJob->SetRefCountBuffer( pRefCountBuffer );
|
|
|
|
DNQueueWorkerJob(pdnObject,pWorkerJob);
|
|
pWorkerJob = NULL;
|
|
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DNASSERT( pNTOp == NULL );
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pLocalPlayer)
|
|
{
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
}
|
|
if (pRefCountBuffer)
|
|
{
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
if (pPending)
|
|
{
|
|
pPending->ReturnSelfToPool();
|
|
pPending = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformHostMigration2
|
|
//
|
|
// Resynchronize NameTables of all connected players
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformHostMigration2"
|
|
|
|
HRESULT DNPerformHostMigration2(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
HRESULT hResultCode;
|
|
CRefCountBuffer *pRefCountBuffer;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pLocalPlayer;
|
|
CNameTableEntry *pNTEntry;
|
|
CNameTableEntry *pNTELatest;
|
|
CConnection *pConnection;
|
|
DN_INTERNAL_MESSAGE_REQ_NAMETABLE_OP *pInfo;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: (none)");
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pRefCountBuffer = NULL;
|
|
pLocalPlayer = NULL;
|
|
pNTEntry = NULL;
|
|
pNTELatest = NULL;
|
|
pConnection = NULL;
|
|
|
|
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get local player reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// See if we (Host player) have the latest NameTable
|
|
//
|
|
pLocalPlayer->AddRef();
|
|
pNTELatest = pLocalPlayer;
|
|
DPFX(DPFPREP, 7,"Seed latest version [%ld] - player [0x%lx]",pNTELatest->GetLatestVersion(),pNTELatest->GetDPNID());
|
|
|
|
pdnObject->NameTable.ReadLock();
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && (pNTEntry->GetLatestVersion() > pNTELatest->GetLatestVersion()))
|
|
{
|
|
//
|
|
// Only want players we can actually reach !
|
|
//
|
|
pNTEntry->Unlock();
|
|
if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
if (!pConnection->IsDisconnecting() && !pConnection->IsInvalid())
|
|
{
|
|
pNTELatest->Release();
|
|
pNTEntry->AddRef();
|
|
pNTELatest = pNTEntry;
|
|
|
|
DPFX(DPFPREP, 7,"New latest version [%ld] - player [0x%lx]",
|
|
pNTELatest->GetLatestVersion(),pNTELatest->GetDPNID());
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(pConnection == NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNTEntry->Unlock();
|
|
}
|
|
pNTEntry = NULL;
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
if (!pNTELatest->IsLocal())
|
|
{
|
|
// Request missing entries from player w/ latest NameTable
|
|
DPFX(DPFPREP, 7,"Host DOES NOT have latest NameTable !");
|
|
DPFX(DPFPREP, 7,"Host has [%ld], player [0x%lx] has [%ld]",pLocalPlayer->GetLatestVersion(),
|
|
pNTELatest->GetDPNID(),pNTELatest->GetLatestVersion());
|
|
|
|
// Create REQ
|
|
hResultCode = RefCountBufferNew(pdnObject,
|
|
sizeof(DN_INTERNAL_MESSAGE_REQ_NAMETABLE_OP),
|
|
MemoryBlockAlloc,
|
|
MemoryBlockFree,
|
|
&pRefCountBuffer);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not create RefCount buffer for NameTable re-sync");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pInfo = reinterpret_cast<DN_INTERNAL_MESSAGE_REQ_NAMETABLE_OP*>(pRefCountBuffer->GetBufferAddress());
|
|
pInfo->dwVersion = pLocalPlayer->GetLatestVersion() + 1;
|
|
pInfo->dwVersionNotUsed = 0;
|
|
|
|
// Send REQ
|
|
if ((hResultCode = pNTELatest->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
hResultCode = DNSendMessage(pdnObject,
|
|
pConnection,
|
|
DN_MSG_INTERNAL_REQ_NAMETABLE_OP,
|
|
pNTELatest->GetDPNID(),
|
|
pRefCountBuffer->BufferDescAddress(),
|
|
1,
|
|
pRefCountBuffer,
|
|
0,
|
|
DN_SENDFLAGS_RELIABLE,
|
|
NULL,
|
|
NULL);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not send NameTable re-sync REQ");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
|
|
pRefCountBuffer->Release(); // Added 19/07/00 MJN - is this needed ?
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP, 7,"Host has latest NameTable - proceed with Host Migration");
|
|
hResultCode = DNPerformHostMigration3(pdnObject,NULL);
|
|
}
|
|
|
|
pNTELatest->Release();
|
|
pNTELatest = NULL;
|
|
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pRefCountBuffer)
|
|
{
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
if (pLocalPlayer)
|
|
{
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
}
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
if (pNTELatest)
|
|
{
|
|
pNTELatest->Release();
|
|
pNTELatest = NULL;
|
|
}
|
|
if (pConnection)
|
|
{
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNPerformHostMigration3
|
|
//
|
|
// Finish host migration.
|
|
// - perform missing NameTable operations and pass them on
|
|
// - send pending operations
|
|
// - inform players that host migration is complete
|
|
// - complete (local) outstanding async operations
|
|
// - initiate new listen (if required) to handle enums on known port
|
|
// As they say on the TTC, "All operations have returned to normal."
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNPerformHostMigration3"
|
|
|
|
HRESULT DNPerformHostMigration3(DIRECTNETOBJECT *const pdnObject,
|
|
void *const pMsg)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CNameTableEntry **PlayerList;
|
|
CNameTableEntry *pNTEntry;
|
|
CConnection *pConnection;
|
|
CPendingDeletion *pPending;
|
|
DWORD dwNameTableVersion;
|
|
DWORD dw;
|
|
DWORD dwCount;
|
|
DWORD dwActual;
|
|
DWORD dwUpdateFlags;
|
|
CNameTableOp *pNTOp;
|
|
UNALIGNED DN_NAMETABLE_OP_INFO *pInfo;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP *pAck;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pMsg [0x%p]",pMsg);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pNTOp = NULL;
|
|
pNTEntry = NULL;
|
|
pConnection = NULL;
|
|
pPending = NULL;
|
|
PlayerList = NULL;
|
|
|
|
//
|
|
// Update missing NameTable operations on Host
|
|
//
|
|
if (pMsg != NULL)
|
|
{
|
|
pAck = static_cast<DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP*>(pMsg);
|
|
DPFX(DPFPREP, 7,"Number of missing NameTable entries [%ld]",pAck->dwNumEntries);
|
|
pInfo = reinterpret_cast<DN_NAMETABLE_OP_INFO*>(pAck+1);
|
|
for (dw = 0; dw < pAck->dwNumEntries ; dw++)
|
|
{
|
|
DPFX(DPFPREP, 7,"Adding missing entry [%ld] of [%ld]",dw+1,pAck->dwNumEntries);
|
|
hResultCode = DNNTAddOperation( pdnObject,
|
|
pInfo->dwMsgId,
|
|
static_cast<void*>(reinterpret_cast<BYTE*>(pMsg) + pInfo->dwOpOffset),
|
|
pInfo->dwOpSize,
|
|
0,
|
|
NULL );
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not add missing NameTable operation - ignore and continue");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
}
|
|
pInfo++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update missing NameTable operations on other players
|
|
//
|
|
|
|
//
|
|
// Determine player list
|
|
//
|
|
dwCount = 0;
|
|
dwActual = 0;
|
|
pdnObject->NameTable.ReadLock();
|
|
dwNameTableVersion = pdnObject->NameTable.GetVersion();
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && (pNTEntry->GetLatestVersion() < dwNameTableVersion))
|
|
{
|
|
dwCount++;
|
|
}
|
|
pNTEntry->Unlock();
|
|
pNTEntry = NULL;
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
if (dwCount)
|
|
{
|
|
if ((PlayerList = static_cast<CNameTableEntry**>(DNMalloc(dwCount * sizeof(CNameTableEntry*)))) == NULL)
|
|
{
|
|
DPFERR("Could not allocate target list");
|
|
DNASSERT(FALSE);
|
|
pdnObject->NameTable.Unlock();
|
|
hResultCode = DPN_OK;
|
|
goto Exit;
|
|
}
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && (pNTEntry->GetLatestVersion() < dwNameTableVersion))
|
|
{
|
|
DNASSERT(dwActual < dwCount);
|
|
pNTEntry->AddRef();
|
|
PlayerList[dwActual] = pNTEntry;
|
|
dwActual++;
|
|
}
|
|
pNTEntry->Unlock();
|
|
pNTEntry = NULL;
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
//
|
|
// Send missing entries to players in player list
|
|
//
|
|
for (dwCount = 0 ; dwCount < dwActual ; dwCount++)
|
|
{
|
|
//
|
|
// Ensure player is reachable
|
|
//
|
|
if ((hResultCode = PlayerList[dwCount]->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
if (!pConnection->IsDisconnecting() && !pConnection->IsInvalid())
|
|
{
|
|
DPFX(DPFPREP, 7,"Player [0x%lx] is missing entries: dwLatestVersion [%ld] should be [%ld]",
|
|
PlayerList[dwCount]->GetDPNID(),PlayerList[dwCount]->GetLatestVersion(),dwNameTableVersion);
|
|
|
|
// Send required entries
|
|
for ( dw = PlayerList[dwCount]->GetLatestVersion() + 1 ; dw <= dwNameTableVersion ; dw++ )
|
|
{
|
|
DPFX(DPFPREP, 7,"Send entry [%ld] to player [0x%lx]",dw,PlayerList[dwCount]->GetDPNID());
|
|
|
|
// Get entry to send
|
|
pNTOp = NULL;
|
|
if ((hResultCode = DNNTFindOperation(pdnObject,dw,&pNTOp)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not retrieve NameTable operation - advance to next player");
|
|
DisplayDNError(0,hResultCode);
|
|
break;
|
|
}
|
|
|
|
hResultCode = DNSendMessage(pdnObject,
|
|
pConnection,
|
|
pNTOp->GetMsgId(),
|
|
PlayerList[dwCount]->GetDPNID(),
|
|
pNTOp->GetRefCountBuffer()->BufferDescAddress(),
|
|
1,
|
|
pNTOp->GetRefCountBuffer(),
|
|
0,
|
|
DN_SENDFLAGS_RELIABLE,
|
|
NULL,
|
|
NULL);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not send missing NameTable entry - advance to next player");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
|
|
DNASSERT(FALSE);
|
|
break;
|
|
}
|
|
} // for
|
|
} // if
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
} // if
|
|
PlayerList[dwCount]->Release();
|
|
PlayerList[dwCount] = NULL;
|
|
}
|
|
if (PlayerList != NULL)
|
|
{
|
|
DNFree(PlayerList);
|
|
PlayerList = NULL;
|
|
}
|
|
|
|
//
|
|
// Host finished migration process
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~(DN_OBJECT_FLAG_HOST_MIGRATING | DN_OBJECT_FLAG_HOST_MIGRATING_2));
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Exit;
|
|
}
|
|
DNASSERT(pdnObject->pNewHost != NULL); // This should be set !
|
|
pdnObject->pNewHost->Release();
|
|
pdnObject->pNewHost = NULL;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Cleanup NameTable
|
|
//
|
|
DPFX(DPFPREP, 7,"Cleaning up NameTable");
|
|
hResultCode = DNCleanUpNameTable(pdnObject);
|
|
|
|
//
|
|
// Send pending deletions
|
|
//
|
|
DPFX(DPFPREP, 7,"Running pending operations");
|
|
DNEnterCriticalSection(&pdnObject->csNameTableOpList);
|
|
pBilink = pdnObject->m_bilinkPendingDeletions.GetNext();
|
|
while (pBilink != &pdnObject->m_bilinkPendingDeletions)
|
|
{
|
|
pPending = CONTAINING_OBJECT(pBilink,CPendingDeletion,m_bilinkPendingDeletions);
|
|
|
|
pPending->m_bilinkPendingDeletions.RemoveFromList();
|
|
|
|
DNLeaveCriticalSection(&pdnObject->csNameTableOpList);
|
|
|
|
DNHostDisconnect(pdnObject,pPending->GetDPNID(),DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER);
|
|
pPending->ReturnSelfToPool();
|
|
pPending = NULL;
|
|
|
|
DNEnterCriticalSection(&pdnObject->csNameTableOpList);
|
|
pBilink = pdnObject->m_bilinkPendingDeletions.GetNext();
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csNameTableOpList);
|
|
|
|
//
|
|
// Inform connected players that Host migration is complete
|
|
//
|
|
DPFX(DPFPREP, 7,"Sending HOST_MIGRATE_COMPLETE messages");
|
|
hResultCode = DNSendHostMigrateCompleteMessage(pdnObject);
|
|
|
|
//
|
|
// Ensure outstanding operations complete
|
|
//
|
|
DPFX(DPFPREP, 7,"Completing outstanding operations");
|
|
hResultCode = DNCompleteOutstandingOperations(pdnObject);
|
|
|
|
//
|
|
// Update listens
|
|
//
|
|
dwUpdateFlags = 0;
|
|
#ifndef DPNBUILD_SINGLEPROCESS
|
|
if(pdnObject->ApplicationDesc.UseDPNSVR())
|
|
{
|
|
dwUpdateFlags |= DN_UPDATE_LISTEN_FLAG_DPNSVR;
|
|
}
|
|
#endif // ! DPNBUILD_SINGLEPROCESS
|
|
if (pdnObject->ApplicationDesc.DisallowEnums())
|
|
{
|
|
dwUpdateFlags |= DN_UPDATE_LISTEN_FLAG_DISALLOW_ENUMS;
|
|
}
|
|
if (dwUpdateFlags)
|
|
{
|
|
DNUpdateListens(pdnObject,dwUpdateFlags);
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DNASSERT(pNTEntry == NULL);
|
|
DNASSERT(pConnection == NULL);
|
|
DNASSERT(pPending == NULL);
|
|
DNASSERT(PlayerList == NULL);
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
// DNProcessHostMigration1
|
|
//
|
|
// Perform an instructed host migration
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNProcessHostMigration1"
|
|
|
|
HRESULT DNProcessHostMigration1(DIRECTNETOBJECT *const pdnObject,
|
|
void *const pvMsg)
|
|
{
|
|
HRESULT hResultCode;
|
|
CNameTableEntry *pNTEntry;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_HOST_MIGRATE *pInfo;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pvMsg [0x%p]",pvMsg);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pvMsg != NULL);
|
|
|
|
pNTEntry = NULL;
|
|
|
|
pInfo = static_cast<DN_INTERNAL_MESSAGE_HOST_MIGRATE*>(pvMsg);
|
|
|
|
DPFX(DPFPREP, 7,"New Host [0x%lx], Old Host [0x%lx]",pInfo->dpnidNewHost,pInfo->dpnidOldHost);
|
|
|
|
DNASSERT(pInfo->dpnidNewHost != NULL);
|
|
DNASSERT(pInfo->dpnidOldHost != NULL);
|
|
|
|
//
|
|
// Update destruction of old host as normal, and disconnect if possible
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.FindEntry(pInfo->dpnidOldHost,&pNTEntry)) == DPN_OK)
|
|
{
|
|
CConnection *pConnection;
|
|
|
|
pConnection = NULL;
|
|
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYPLAYERREASON_NORMAL );
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
if (pConnection->IsConnected())
|
|
{
|
|
pConnection->Disconnect();
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
DNASSERT( pConnection == NULL );
|
|
}
|
|
|
|
//
|
|
// Get new host entry
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.FindEntry(pInfo->dpnidNewHost,&pNTEntry)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not find new host NameTableEntry");
|
|
DisplayDNError(0,hResultCode);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Set HOST_MIGRATE status on DirectNet object
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING)
|
|
{
|
|
//
|
|
// If we are already host migrating, ensure that this message
|
|
// is not from on older host than we expect. If it is, we
|
|
// will ignore it, and continue
|
|
//
|
|
DNASSERT(pdnObject->pNewHost != NULL);
|
|
if (pdnObject->pNewHost->GetVersion() > pNTEntry->GetVersion())
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Failure;
|
|
}
|
|
pdnObject->pNewHost->Release();
|
|
pdnObject->pNewHost = NULL;
|
|
}
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_HOST_MIGRATING;
|
|
DNASSERT( pdnObject->pNewHost == NULL );
|
|
pNTEntry->AddRef();
|
|
pdnObject->pNewHost = pNTEntry;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
// Delete old host
|
|
pdnObject->NameTable.DeletePlayer(pInfo->dpnidOldHost,NULL);
|
|
|
|
#ifndef DPNBUILD_NOLOBBY
|
|
//
|
|
// Indicate to lobby (if there is one) that a host migration has occured
|
|
//
|
|
DNUpdateLobbyStatus(pdnObject,DPLSESSION_HOSTMIGRATED);
|
|
#endif // ! DPNBUILD_NOLOBBY
|
|
|
|
//
|
|
// Make new host
|
|
//
|
|
pdnObject->NameTable.UpdateHostPlayer( pNTEntry );
|
|
|
|
//
|
|
// We will need to wait for all threads performing name table operations to finish running
|
|
// before we send the current name table version to the host player
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_HOST_MIGRATING_WAIT;
|
|
pdnObject->dwWaitingThreadID = GetCurrentThreadId();
|
|
if (pdnObject->dwRunningOpCount > 0)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DPFX(DPFPREP,7,"Waiting for running threads to finish");
|
|
IDirectPlay8ThreadPoolWork_WaitWhileWorking(pdnObject->pIDPThreadPoolWork,
|
|
HANDLE_FROM_DNHANDLE(pdnObject->hRunningOpEvent),
|
|
0);
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP,7,"No running threads to wait for");
|
|
}
|
|
if (pdnObject->dwWaitingThreadID == GetCurrentThreadId())
|
|
{
|
|
CBilink *pBilink;
|
|
CNameTableOp *pNTOp;
|
|
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
//
|
|
// Clean-up outstanding operations
|
|
//
|
|
DPFX(DPFPREP,7,"Cleaning up outstanding NameTable operations");
|
|
pdnObject->NameTable.WriteLock();
|
|
DPFX(DPFPREP,7,"NameTable version [%ld]",pdnObject->NameTable.GetVersion());
|
|
pBilink = pdnObject->NameTable.m_bilinkNameTableOps.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkNameTableOps)
|
|
{
|
|
pNTOp = CONTAINING_OBJECT(pBilink,CNameTableOp,m_bilinkNameTableOps);
|
|
pBilink = pBilink->GetNext();
|
|
if (pNTOp->GetVersion() > pdnObject->NameTable.GetVersion())
|
|
{
|
|
DPFX(DPFPREP,7,"Removing outstanding operation [0x%p], version [%ld]",pNTOp,pNTOp->GetVersion());
|
|
pNTOp->m_bilinkNameTableOps.RemoveFromList();
|
|
if (pNTOp->GetRefCountBuffer())
|
|
{
|
|
pNTOp->GetRefCountBuffer()->Release();
|
|
pNTOp->SetRefCountBuffer( NULL );
|
|
}
|
|
if (pNTOp->GetSP())
|
|
{
|
|
pNTOp->GetSP()->Release();
|
|
pNTOp->SetSP( NULL );
|
|
}
|
|
pNTOp->ReturnSelfToPool();
|
|
}
|
|
pNTOp = NULL;
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_HOST_MIGRATING_WAIT);
|
|
pdnObject->dwWaitingThreadID = 0;
|
|
DNResetEvent(pdnObject->hRunningOpEvent);
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't continue because a newer host migration on another thread is now waiting
|
|
//
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DPFX(DPFPREP,7,"Looks like a newer host migration is running - exiting");
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Send NameTable version to Host player
|
|
//
|
|
DNNTPlayerSendVersion(pdnObject);
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNProcessHostMigration2
|
|
//
|
|
// Send Host player NameTable entries missing from its (Host's) NameTable
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNProcessHostMigration2"
|
|
|
|
HRESULT DNProcessHostMigration2(DIRECTNETOBJECT *const pdnObject,
|
|
void *const pMsg)
|
|
{
|
|
HRESULT hResultCode;
|
|
DWORD dwAckMsgSize;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pHostPlayer;
|
|
CConnection *pConnection;
|
|
CRefCountBuffer *pRefCountBuffer;
|
|
CPackedBuffer PackedBuffer;
|
|
CNameTableOp *pNTOp;
|
|
DN_NAMETABLE_OP_INFO *pInfo;
|
|
UNALIGNED DN_INTERNAL_MESSAGE_REQ_NAMETABLE_OP *pReq;
|
|
DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP *pAck;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pMsg [0x%p]",pMsg);
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
DNASSERT(pMsg != NULL);
|
|
|
|
pHostPlayer = NULL;
|
|
pConnection = NULL;
|
|
pRefCountBuffer = NULL;
|
|
|
|
pReq = static_cast<DN_INTERNAL_MESSAGE_REQ_NAMETABLE_OP*>(pMsg);
|
|
|
|
DPFX(DPFPREP, 5,"Host requested NameTable ops [%ld] to [%ld]",
|
|
pReq->dwVersion,pdnObject->NameTable.GetVersion());
|
|
|
|
//
|
|
// Determine ACK message size
|
|
//
|
|
pdnObject->NameTable.ReadLock();
|
|
DNASSERT(pdnObject->NameTable.GetVersion() >= pReq->dwVersion);
|
|
dwAckMsgSize = sizeof(DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP); // Number of entries
|
|
dwAckMsgSize += ((pdnObject->NameTable.GetVersion() - pReq->dwVersion + 1) // Message info
|
|
* sizeof(DN_NAMETABLE_OP_INFO));
|
|
|
|
pBilink = pdnObject->NameTable.m_bilinkNameTableOps.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkNameTableOps)
|
|
{
|
|
pNTOp = CONTAINING_OBJECT(pBilink,CNameTableOp,m_bilinkNameTableOps);
|
|
if ((pNTOp->GetVersion() >= pReq->dwVersion) && (pNTOp->GetVersion() <= pdnObject->NameTable.GetVersion()))
|
|
{
|
|
DNASSERT(pNTOp->GetRefCountBuffer() != NULL);
|
|
dwAckMsgSize += pNTOp->GetRefCountBuffer()->GetBufferSize();
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
//
|
|
// Create ACK buffer
|
|
//
|
|
if ((hResultCode = RefCountBufferNew(pdnObject,dwAckMsgSize,MemoryBlockAlloc,MemoryBlockFree,&pRefCountBuffer)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not create RefCount buffer for NameTable re-sync ACK");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
pdnObject->NameTable.Unlock();
|
|
goto Failure;
|
|
}
|
|
PackedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(),pRefCountBuffer->GetBufferSize());
|
|
pAck = reinterpret_cast<DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP *>(pRefCountBuffer->GetBufferAddress());
|
|
|
|
// Header
|
|
pAck->dwNumEntries = pdnObject->NameTable.GetVersion() - pReq->dwVersion + 1;
|
|
PackedBuffer.AddToFront(NULL,sizeof(DN_INTERNAL_MESSAGE_ACK_NAMETABLE_OP));
|
|
|
|
// Offset list
|
|
pInfo = reinterpret_cast<DN_NAMETABLE_OP_INFO*>(PackedBuffer.GetHeadAddress());
|
|
PackedBuffer.AddToFront(NULL,pAck->dwNumEntries * sizeof(DN_NAMETABLE_OP_INFO));
|
|
|
|
// Messages
|
|
pBilink = pdnObject->NameTable.m_bilinkNameTableOps.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkNameTableOps)
|
|
{
|
|
pNTOp = CONTAINING_OBJECT(pBilink,CNameTableOp,m_bilinkNameTableOps);
|
|
if ((pNTOp->GetVersion() >= pReq->dwVersion) && (pNTOp->GetVersion() <= pdnObject->NameTable.GetVersion()))
|
|
{
|
|
DNASSERT(pNTOp->GetRefCountBuffer() != NULL);
|
|
if ((hResultCode = PackedBuffer.AddToBack(pNTOp->GetRefCountBuffer()->GetBufferAddress(),
|
|
pNTOp->GetRefCountBuffer()->GetBufferSize())) != DPN_OK)
|
|
{
|
|
DPFERR("Could not fill NameTable re-sync ACK");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
pdnObject->NameTable.Unlock();
|
|
goto Failure;
|
|
}
|
|
pInfo->dwMsgId = pNTOp->GetMsgId();
|
|
pInfo->dwOpOffset = PackedBuffer.GetTailOffset();
|
|
pInfo->dwOpSize = pNTOp->GetRefCountBuffer()->GetBufferSize();
|
|
pInfo++;
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
// Send ACK buffer
|
|
if ((hResultCode = pdnObject->NameTable.GetHostPlayerRef( &pHostPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not find Host player");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
if ((hResultCode = pHostPlayer->GetConnectionRef( &pConnection )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get Connection reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
hResultCode = DNSendMessage(pdnObject,
|
|
pConnection,
|
|
DN_MSG_INTERNAL_ACK_NAMETABLE_OP,
|
|
pHostPlayer->GetDPNID(),
|
|
pRefCountBuffer->BufferDescAddress(),
|
|
1,
|
|
pRefCountBuffer,
|
|
0,
|
|
DN_SENDFLAGS_RELIABLE,
|
|
NULL,
|
|
NULL);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not send NameTable re-sync ACK");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
|
|
pRefCountBuffer->Release(); // Added 19/07/00 MJN - Is this needed ?
|
|
pRefCountBuffer = NULL;
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pHostPlayer)
|
|
{
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
}
|
|
if (pConnection)
|
|
{
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
if (pRefCountBuffer)
|
|
{
|
|
pRefCountBuffer->Release();
|
|
pRefCountBuffer = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNProcessHostMigration3
|
|
//
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNProcessHostMigration3"
|
|
|
|
HRESULT DNProcessHostMigration3(DIRECTNETOBJECT *const pdnObject,
|
|
const DPNID dpnid)
|
|
{
|
|
HRESULT hResultCode;
|
|
CNameTableEntry *pNTEntry;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%lx]",dpnid);
|
|
|
|
pNTEntry = NULL;
|
|
|
|
// No longer in Host migration mode
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
pdnObject->dwFlags &= (~DN_OBJECT_FLAG_HOST_MIGRATING);
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_CLOSING)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
hResultCode = DPN_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If we receive a HOST_MIGRATION_COMPLETE, we need to ensure that it is for the current host migration,
|
|
// and that another one hasn't started after.
|
|
// If this is the correct new host (i.e. pdnObject->pNewHost != NULL and DPNID's match),
|
|
// then we will continue. Otherwise, we will exit this function.
|
|
//
|
|
if (pdnObject->pNewHost)
|
|
{
|
|
if (pdnObject->pNewHost->GetDPNID() == dpnid)
|
|
{
|
|
pNTEntry = pdnObject->pNewHost;
|
|
pdnObject->pNewHost = NULL;
|
|
}
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
//
|
|
// Complete outstanding operations
|
|
//
|
|
DPFX(DPFPREP, 7,"Completing outstanding operations");
|
|
hResultCode = DNCompleteOutstandingOperations(pdnObject);
|
|
}
|
|
else
|
|
{
|
|
DPFX(DPFPREP,7,"Host migration completed by wrong new host - ignore and continue");
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DNASSERT( pNTEntry == NULL );
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
// DNCompleteOutstandingOperations
|
|
//
|
|
// We will attempt to complete any outstanding asynchronous NameTable operations
|
|
// (i.e. create/destroy group, add/delete player to/from group, update info).
|
|
// If we are the Host player,
|
|
// - try processing the request directly
|
|
// - release the async op to generate completions
|
|
// Otherwise
|
|
// - resend the request to the Host
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCompleteOutstandingOperations"
|
|
|
|
HRESULT DNCompleteOutstandingOperations(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CAsyncOp *pAsyncOp;
|
|
CAsyncOp *pSend;
|
|
CAsyncOp **RequestList;
|
|
CConnection *pConnection;
|
|
CNameTableEntry *pHostPlayer;
|
|
CNameTableEntry *pLocalPlayer;
|
|
DN_SEND_OP_DATA *pSendOpData;
|
|
DWORD dwCount;
|
|
DWORD dwActual;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: none");
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pAsyncOp = NULL;
|
|
pSend = NULL;
|
|
RequestList = NULL;
|
|
pConnection = NULL;
|
|
pHostPlayer = NULL;
|
|
pLocalPlayer = NULL;
|
|
|
|
//
|
|
// Get Host connection
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.GetHostPlayerRef( &pHostPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get host player reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
if ((hResultCode = pHostPlayer->GetConnectionRef( &pConnection )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get host connection reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
|
|
//
|
|
// Get local player
|
|
//
|
|
if ((hResultCode = pdnObject->NameTable.GetLocalPlayerRef( &pLocalPlayer )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get local player reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
|
|
dwCount = 0;
|
|
dwActual = 0;
|
|
|
|
//
|
|
// Determine outstanding request list size and build it
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
pBilink = pdnObject->m_bilinkRequestList.GetNext();
|
|
while (pBilink != &pdnObject->m_bilinkRequestList)
|
|
{
|
|
dwCount++;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
if (dwCount > 0)
|
|
{
|
|
if ((RequestList = static_cast<CAsyncOp**>(MemoryBlockAlloc(pdnObject,dwCount * sizeof(CAsyncOp*)))) == NULL)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
DPFERR("Could not allocate request list");
|
|
hResultCode = DPNERR_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
pBilink = pdnObject->m_bilinkRequestList.GetNext();
|
|
while (pBilink != &pdnObject->m_bilinkRequestList)
|
|
{
|
|
pAsyncOp = CONTAINING_OBJECT(pBilink,CAsyncOp,m_bilinkActiveList);
|
|
DNASSERT(dwActual < dwCount);
|
|
DNASSERT(pAsyncOp->GetOpType() == ASYNC_OP_REQUEST);
|
|
pAsyncOp->AddRef();
|
|
RequestList[dwActual] = pAsyncOp;
|
|
pAsyncOp = NULL;
|
|
dwActual++;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
//
|
|
// Perform outstanding requests
|
|
//
|
|
|
|
if (RequestList)
|
|
{
|
|
DWORD dw;
|
|
|
|
for ( dw = 0 ; dw < dwActual ; dw++ )
|
|
{
|
|
pSendOpData = RequestList[dw]->GetLocalSendOpData();
|
|
if ( pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_CREATE_GROUP
|
|
|| pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_DESTROY_GROUP
|
|
|| pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_ADD_PLAYER_TO_GROUP
|
|
|| pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_DELETE_PLAYER_FROM_GROUP
|
|
|| pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_UPDATE_INFO
|
|
|| pSendOpData->dwMsgId == DN_MSG_INTERNAL_REQ_INTEGRITY_CHECK)
|
|
{
|
|
DPFX(DPFPREP, 7,"Found outstanding operation: dwMsgId [0x%lx]",pSendOpData->dwMsgId);
|
|
|
|
if (pLocalPlayer->IsHost())
|
|
{
|
|
//
|
|
// Remove request from request list
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csActiveList);
|
|
RequestList[dw]->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&pdnObject->csActiveList);
|
|
|
|
hResultCode = DNHostProcessRequest( pdnObject,
|
|
pSendOpData->dwMsgId,
|
|
pSendOpData->BufferDesc[1].pBufferData,
|
|
pLocalPlayer->GetDPNID() );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// re-SEND REQUEST
|
|
//
|
|
hResultCode = DNPerformChildSend( pdnObject,
|
|
RequestList[dw],
|
|
pConnection,
|
|
0,
|
|
&pSend,
|
|
TRUE);
|
|
if (hResultCode == DPNERR_PENDING)
|
|
{
|
|
//
|
|
// Reset SEND AsyncOp to complete apropriately.
|
|
//
|
|
pSend->SetCompletion( DNCompleteSendRequest );
|
|
|
|
pSend->Release();
|
|
pSend = NULL;
|
|
}
|
|
}
|
|
}
|
|
pSendOpData = NULL;
|
|
RequestList[dw]->Release();
|
|
RequestList[dw] = NULL;
|
|
}
|
|
MemoryBlockFree(pdnObject,RequestList);
|
|
RequestList = NULL;
|
|
}
|
|
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pLocalPlayer)
|
|
{
|
|
pLocalPlayer->Release();
|
|
pLocalPlayer = NULL;
|
|
}
|
|
if (pHostPlayer)
|
|
{
|
|
pHostPlayer->Release();
|
|
pHostPlayer = NULL;
|
|
}
|
|
if (pConnection)
|
|
{
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
if (RequestList)
|
|
{
|
|
MemoryBlockFree(pdnObject,RequestList);
|
|
RequestList = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
// DNCheckReceivedAllVersions
|
|
//
|
|
// Check to see if all players in the NameTable have returned their
|
|
// NameTable versions. If so, ensure the NameTables are re-sync'd and
|
|
// then finish the Host migration
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCheckReceivedAllVersions"
|
|
|
|
HRESULT DNCheckReceivedAllVersions(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
BOOL bNotReady;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: (none)");
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
bNotReady = FALSE;
|
|
pdnObject->NameTable.ReadLock();
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while ((pBilink != &pdnObject->NameTable.m_bilinkPlayers) && (!bNotReady))
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && (pNTEntry->GetLatestVersion() == 0))
|
|
{
|
|
//
|
|
// Ensure that we are not including dropped/disconnected players who have yet to be processed
|
|
//
|
|
CConnection *pConnection;
|
|
|
|
pConnection = NULL;
|
|
|
|
pNTEntry->Unlock();
|
|
if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
if (!pConnection->IsDisconnecting() && !pConnection->IsInvalid())
|
|
{
|
|
DPFX(DPFPREP, 7,"Player [0x%lx] has not returned dwLatestVersion",pNTEntry->GetDPNID());
|
|
bNotReady = TRUE; // these must all be non-zero
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNTEntry->Unlock();
|
|
}
|
|
pNTEntry = NULL;
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
if (bNotReady)
|
|
{
|
|
DPFX(DPFPREP, 7,"All players have not responded");
|
|
return(DPNERR_PENDING);
|
|
}
|
|
|
|
//
|
|
// Ensure only one thread runs this from here on out
|
|
//
|
|
DNEnterCriticalSection(&pdnObject->csDirectNetObject);
|
|
if (!(pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING))
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DPFX(DPFPREP, 7,"Another thread has already finished Host Migration - returning");
|
|
hResultCode = DPN_OK;
|
|
goto Exit;
|
|
}
|
|
if (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING_2)
|
|
{
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
DPFX(DPFPREP, 7,"Another thread will proceed with Host Migration - returning");
|
|
hResultCode = DPN_OK;
|
|
goto Exit;
|
|
}
|
|
pdnObject->dwFlags |= DN_OBJECT_FLAG_HOST_MIGRATING_2;
|
|
DNLeaveCriticalSection(&pdnObject->csDirectNetObject);
|
|
|
|
DPFX(DPFPREP, 7,"All players have responded - host migration stage 1 complete");
|
|
hResultCode = DNPerformHostMigration2(pdnObject);
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
// DNCleanUpNameTable
|
|
//
|
|
// Clean up the NameTable.
|
|
// There are some cases where dropped players are not properly removed from the NameTable.
|
|
// An example is when the Host-elect drops right after the Host before it has a chance
|
|
// to send out a HOST_MIGRATE message. In this case, since the HOST_MIGRATE message
|
|
// implicitly includes the DELETE_PLAYER message for the original Host, the original
|
|
// Host player never gets removed from current players' NameTables, though it DOES get
|
|
// marked as DISCONNECTING.
|
|
// Delete all DISCONNECTING players with older NameTable versions than ourselves.
|
|
// Players with newer NameTable versions imply WE are the Host player, so we will
|
|
// take care of them later (Pending Operations)
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNCleanUpNameTable"
|
|
|
|
HRESULT DNCleanUpNameTable(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
DPNID *List;
|
|
DWORD dwCount;
|
|
DWORD dwActual;
|
|
DWORD dw;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: (none)");
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
List = NULL;
|
|
|
|
//
|
|
// Create list of old player DPNID's
|
|
//
|
|
dwCount = 0;
|
|
dwActual = 0;
|
|
pdnObject->NameTable.ReadLock();
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->IsDisconnecting() && (pNTEntry->GetVersion() < pdnObject->NameTable.GetLocalPlayer()->GetVersion()))
|
|
{
|
|
DPFX(DPFPREP, 7,"Found old player [0x%lx]",pNTEntry->GetDPNID());
|
|
dwCount++;
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
if (dwCount)
|
|
{
|
|
if ((List = static_cast<DPNID*>(DNMalloc(dwCount * sizeof(DPNID*)))) != NULL)
|
|
{
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->IsDisconnecting() && (pNTEntry->GetVersion() < pdnObject->NameTable.GetLocalPlayer()->GetVersion()))
|
|
{
|
|
DNASSERT(dwActual < dwCount);
|
|
List[dwActual] = pNTEntry->GetDPNID();
|
|
dwActual++;
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
}
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
//
|
|
// Remove old players
|
|
//
|
|
if (List)
|
|
{
|
|
for (dw = 0 ; dw < dwActual ; dw++)
|
|
{
|
|
DPFX(DPFPREP, 7,"Removing old player [0x%lx]",List[dw]);
|
|
DNHostDisconnect(pdnObject,List[dw],DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER);
|
|
List[dw] = 0;
|
|
}
|
|
|
|
DNFree(List);
|
|
List = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
DNASSERT(List == NULL);
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
// DNSendHostMigrateCompleteMessage
|
|
//
|
|
// Send a HOST_MIGRATE_COMPLETE message to connected players
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DNSendHostMigrateCompleteMessage"
|
|
|
|
HRESULT DNSendHostMigrateCompleteMessage(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
HRESULT hResultCode;
|
|
CAsyncOp *pParent;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
CConnection *pConnection;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: (none)");
|
|
|
|
DNASSERT(pdnObject != NULL);
|
|
|
|
pParent = NULL;
|
|
pNTEntry = NULL;
|
|
pConnection = NULL;
|
|
|
|
hResultCode = DNCreateSendParent( pdnObject,
|
|
DN_MSG_INTERNAL_HOST_MIGRATE_COMPLETE,
|
|
NULL,
|
|
0,
|
|
DN_SENDFLAGS_RELIABLE,
|
|
&pParent);
|
|
if (hResultCode != DPN_OK)
|
|
{
|
|
DPFERR("Could not create AsyncOp");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Lock NameTable
|
|
//
|
|
pdnObject->NameTable.ReadLock();
|
|
|
|
pBilink = pdnObject->NameTable.m_bilinkPlayers.GetNext();
|
|
while (pBilink != &pdnObject->NameTable.m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
if (!pNTEntry->IsDisconnecting() && !pNTEntry->IsLocal())
|
|
{
|
|
if ((hResultCode = pNTEntry->GetConnectionRef( &pConnection )) == DPN_OK)
|
|
{
|
|
hResultCode = DNPerformChildSend( pdnObject,
|
|
pParent,
|
|
pConnection,
|
|
0,
|
|
NULL,
|
|
TRUE);
|
|
if (hResultCode != DPNERR_PENDING)
|
|
{
|
|
DPFERR("Could not perform part of group send - ignore and continue");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(hResultCode != DPN_OK); // it was sent guaranteed, it should not return immediately
|
|
}
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
}
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
//
|
|
// Unlock NameTable
|
|
//
|
|
pdnObject->NameTable.Unlock();
|
|
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pParent)
|
|
{
|
|
pParent->Release();
|
|
pParent = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#endif // !DPNBUILD_NOHOSTMIGRATE
|