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.
3250 lines
75 KiB
3250 lines
75 KiB
/*==========================================================================
|
|
*
|
|
* Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: NameTable.cpp
|
|
* Content: NameTable Object
|
|
*@@BEGIN_MSINTERNAL
|
|
* History:
|
|
* Date By Reason
|
|
* ==== == ======
|
|
* 03/11/00 mjn Created
|
|
* 04/09/00 mjn Track outstanding connections in NameTable
|
|
* 04/18/00 mjn CConnection tracks connection status better
|
|
* 04/19/00 mjn PopulateConnection makes the ALL_PLAYERS link valid before posting ADD_PLAYER
|
|
* 05/03/00 mjn Implemented GetHostPlayerRef, GetLocalPlayerRef, GetAllPlayersGroupRef
|
|
* 05/08/00 mjn PopulateConnection() only sets the player's connection if it was previously NULL
|
|
* 05/10/00 mjn Release NameTableEntry lock during notifications in PopulateConnection()
|
|
* 05/16/00 mjn Ensure dpnidGroup is actually a group in IsMember()
|
|
* mjn Better use of locks when clearing short-cut pointers
|
|
* 05/25/00 mjn Fixed infinite loop in UpdateTable()
|
|
* 06/01/00 mjn Added code to validate NameTable array
|
|
* 06/02/00 mjn Fixed logic in GrowNameTable() to handle case of existing free entries
|
|
* 06/07/00 mjn Fixed logic in UpdateTable() to adjust m_dwLastFreeEntry correctly
|
|
* 06/22/00 mjn UnpackNameTableInfo() returns local players DPNID
|
|
* 06/29/00 mjn 64-bit build fixes (2)
|
|
* 07/06/00 mjn Fixed locking problem in CNameTable::MakeLocalPlayer,MakeHostPlayer,MakeAllPlayersGroup
|
|
* 07/07/00 mjn Fixed validation error in FindEntry()
|
|
* 07/20/00 mjn Cleaned up CConnection refcounts and added attempted disconnects
|
|
* mjn Added ClearHostWithDPNID()
|
|
* 07/21/00 mjn Fixed DeletePlayer() to properly handle deleted unconnected players
|
|
* 07/26/00 mjn Moved initialization code from contructor to Initialize()
|
|
* mjn Allow DPNID=0 for FindEntry(), but return DPNERR_DOESNOTEXIST
|
|
* 07/30/00 mjn Set reason codes for destroying players and groups
|
|
* mjn Added hrReason to CNameTable::EmptyTable() and extended clean-up to include short-cut pointers
|
|
* 08/02/00 mjn Dequeue queued messages when propagating CREATE_PLAYER message
|
|
* 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
|
|
* mjn AddPlayerToGroup() does a duplicate check
|
|
* 08/07/00 mjn Wait until player to be added to groups before reducing outstanding connections in PopulateConnection()
|
|
* 08/15/00 mjn Keep group on CGroupConnection objects
|
|
* mjn Clear pGroupConnection from CGroupMembership when removing players from groups
|
|
* 08/23/00 mjn Added CNameTableOp
|
|
* 09/04/00 mjn Added CApplicationDesc
|
|
* 09/05/00 mjn Added m_dpnidMask
|
|
* mjn Removed dwIndex from InsertEntry()
|
|
* 09/06/00 mjn Remove queued messages in EmptyTable() and DeletePlayer()
|
|
* 09/14/00 mjn Added missing pGroupMember->AddRef() in PopulateConnection()
|
|
* 09/17/00 mjn Split m_bilinkEntries into m_bilinkPlayers and m_bilinkGroups
|
|
* mjn Changed AddPlayerToGroup and RemovePlayerFromGroup to use NameTableEntry params
|
|
* 09/26/00 mjn Assume NameTable locks are taken for AddMembership() and RemoveMembership()
|
|
* mjn Attempt to disconnect client from Host in EmptyTable()
|
|
* 09/28/00 mjn Autodestruct groups in DeletePlayer()
|
|
* 10/18/00 mjn Reset m_lOutstandingConnections in UnpackNameTableInfo()
|
|
* 01/11/00 mjn Proper clean up for indicated but not created players in DeletePlayer()
|
|
* 01/25/01 mjn Fixed 64-bit alignment problem when unpacking NameTable
|
|
* 06/02/01 mjn Remove receive buffers from active list in EmptyTable()
|
|
* 06/03/01 mjn Complete and orphan connect parent before releasing in DecOutstandingConnections()
|
|
*@@END_MSINTERNAL
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "dncorei.h"
|
|
|
|
|
|
//**********************************************************************
|
|
// Constant definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Macro definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Structure definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Variable definitions
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function prototypes
|
|
//**********************************************************************
|
|
|
|
//**********************************************************************
|
|
// Function definitions
|
|
//**********************************************************************
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::Initialize"
|
|
|
|
HRESULT CNameTable::Initialize(DIRECTNETOBJECT *const pdnObject)
|
|
{
|
|
m_dwVersion = 1;
|
|
|
|
m_bilinkPlayers.Initialize();
|
|
m_bilinkGroups.Initialize();
|
|
m_bilinkDeleted.Initialize();
|
|
m_bilinkNameTableOps.Initialize();
|
|
|
|
// NOTE: It is important that we call Initialize even if we are going to fail in this
|
|
// function. In other words, don't put anything above this that fails, or you will
|
|
// break CReadWriteLock::Deinitialize.
|
|
if (!m_RWLock.Initialize())
|
|
{
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
//
|
|
// (Pre)allocate a name table entry.
|
|
//
|
|
if (g_NameTableEntryPool.Preallocate(1, pdnObject) < 1)
|
|
{
|
|
DPFX(DPFPREP, 0, "Couldn't allocate default player name table entry!");
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
|
|
NameTableEntryNew(pdnObject,&m_pDefaultPlayer);
|
|
DNASSERT(m_pDefaultPlayer != NULL);
|
|
#else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (NameTableEntryNew(pdnObject,&m_pDefaultPlayer) != DPN_OK)
|
|
{
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|
|
DNASSERT(m_pdnObject == NULL);
|
|
m_pdnObject = pdnObject;
|
|
|
|
return(DPN_OK);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::Deinitialize"
|
|
|
|
void CNameTable::Deinitialize( void )
|
|
{
|
|
if (m_NameTableArray)
|
|
{
|
|
#ifdef DBG
|
|
ValidateArray();
|
|
#endif // DBG
|
|
|
|
DNFree(m_NameTableArray);
|
|
m_NameTableArray = NULL;
|
|
}
|
|
|
|
// Calling this is safe as long as CReadWriteLock::Initialize was called, regardless of
|
|
// whether or not it succeeded.
|
|
m_RWLock.Deinitialize();
|
|
|
|
m_pDefaultPlayer->Release();
|
|
m_pDefaultPlayer = NULL;
|
|
|
|
DNASSERT(m_bilinkPlayers.IsEmpty());
|
|
DNASSERT(m_bilinkGroups.IsEmpty());
|
|
DNASSERT(m_bilinkDeleted.IsEmpty());
|
|
DNASSERT(m_bilinkNameTableOps.IsEmpty());
|
|
|
|
DNASSERT(m_pDefaultPlayer == NULL);
|
|
DNASSERT(m_pLocalPlayer == NULL);
|
|
DNASSERT(m_pHostPlayer == NULL);
|
|
DNASSERT(m_NameTableArray == NULL);
|
|
}
|
|
|
|
|
|
#ifdef DBG
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ValidateArray"
|
|
|
|
void CNameTable::ValidateArray( void )
|
|
{
|
|
DWORD dw;
|
|
DWORD dwFreeEntries;
|
|
|
|
ReadLock();
|
|
|
|
//
|
|
// Ensure free entry count is correct
|
|
//
|
|
dwFreeEntries = 0;
|
|
for (dw = 2 ; dw < m_dwNameTableSize ; dw++)
|
|
{
|
|
if (!(m_NameTableArray[dw].dwFlags & NAMETABLE_ARRAY_ENTRY_FLAG_VALID))
|
|
{
|
|
dwFreeEntries++;
|
|
}
|
|
}
|
|
if (dwFreeEntries != m_dwNumFreeEntries)
|
|
{
|
|
DPFERR("Incorrect number of free entries in NameTable");
|
|
DNASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Ensure free list integrity
|
|
//
|
|
if (m_dwNumFreeEntries)
|
|
{
|
|
dwFreeEntries = 0;
|
|
dw = m_dwFirstFreeEntry;
|
|
do
|
|
{
|
|
if (m_NameTableArray[dw].dwFlags & NAMETABLE_ARRAY_ENTRY_FLAG_VALID)
|
|
{
|
|
DPFERR("Valid entry in NameTable array free list");
|
|
DNASSERT(FALSE);
|
|
}
|
|
dwFreeEntries++;
|
|
dw = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[dw].pNameTableEntry));
|
|
} while (dw != 0);
|
|
|
|
if (dwFreeEntries != m_dwNumFreeEntries)
|
|
{
|
|
DPFERR("Incorrect number of free entries in NameTable array free list");
|
|
DNASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::SetNameTableSize"
|
|
|
|
HRESULT CNameTable::SetNameTableSize( const DWORD dwNumEntries )
|
|
#else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::GrowNameTable"
|
|
|
|
HRESULT CNameTable::GrowNameTable( void )
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
NAMETABLE_ARRAY_ENTRY *pNewArray;
|
|
DWORD dwNewSize;
|
|
DWORD dw;
|
|
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
DNASSERT(m_dwNameTableSize == 0);
|
|
dwNewSize = dwNumEntries + 1; // + 1 because we never hand out entry 0
|
|
#else // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_dwNameTableSize == 0)
|
|
{
|
|
dwNewSize = 2;
|
|
}
|
|
else
|
|
{
|
|
dwNewSize = m_dwNameTableSize * 2;
|
|
}
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|
|
// Allocate new array
|
|
pNewArray = static_cast<NAMETABLE_ARRAY_ENTRY*>(DNMalloc(sizeof(NAMETABLE_ARRAY_ENTRY) * dwNewSize));
|
|
if (pNewArray == NULL)
|
|
{
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_dwNameTableSize > 0)
|
|
{
|
|
DNASSERT(m_NameTableArray != NULL);
|
|
// Copy old array to new array
|
|
memcpy(pNewArray, m_NameTableArray, (sizeof(NAMETABLE_ARRAY_ENTRY) * m_dwNameTableSize));
|
|
}
|
|
else
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
DNASSERT(m_NameTableArray == NULL);
|
|
}
|
|
|
|
//
|
|
// If the array is being grown because there are no free entries, then all of the new free
|
|
// entries will be in the new part of the array. Otherwise, we will need to link the old
|
|
// free list to the new one
|
|
//
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_dwNumFreeEntries != 0)
|
|
{
|
|
// Only new free entries at end of new array
|
|
pNewArray[m_dwLastFreeEntry].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(static_cast<DWORD_PTR>(m_dwNameTableSize));
|
|
}
|
|
else
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
// All free entries at end of new array
|
|
m_dwFirstFreeEntry = m_dwNameTableSize;
|
|
}
|
|
m_dwLastFreeEntry = dwNewSize-1;
|
|
|
|
// Very last FREE entry will not wrap to 0
|
|
pNewArray[m_dwLastFreeEntry].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(0);
|
|
pNewArray[m_dwLastFreeEntry].dwFlags = 0;
|
|
|
|
// Link new FREE entries
|
|
for (dw = m_dwNameTableSize ; dw < m_dwLastFreeEntry ; dw++)
|
|
{
|
|
pNewArray[dw].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(static_cast<DWORD_PTR>(dw+1));
|
|
pNewArray[dw].dwFlags = 0;
|
|
}
|
|
|
|
// Update NameTable
|
|
m_dwNumFreeEntries += (dwNewSize - m_dwNameTableSize);
|
|
m_dwNameTableSize = dwNewSize;
|
|
|
|
// New array
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_NameTableArray)
|
|
{
|
|
DNFree(m_NameTableArray);
|
|
}
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
m_NameTableArray = pNewArray;
|
|
|
|
// We will never allocate 0
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_dwFirstFreeEntry == 0)
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
m_dwFirstFreeEntry = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[m_dwFirstFreeEntry].pNameTableEntry));
|
|
DNASSERT(m_dwNumFreeEntries > 0);
|
|
m_dwNumFreeEntries--;
|
|
}
|
|
|
|
// We will never allocate 1 either, for backwards compatibility
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_dwFirstFreeEntry == 1)
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
m_dwFirstFreeEntry = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[m_dwFirstFreeEntry].pNameTableEntry));
|
|
DNASSERT(m_dwNumFreeEntries > 0);
|
|
m_dwNumFreeEntries--;
|
|
}
|
|
return(DPN_OK);
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ResetNameTable"
|
|
|
|
void CNameTable::ResetNameTable( void )
|
|
{
|
|
DNASSERT(m_pdnObject != NULL);
|
|
DNASSERT(m_pDefaultPlayer != NULL);
|
|
DNASSERT(m_pLocalPlayer == NULL);
|
|
DNASSERT(m_pHostPlayer == NULL);
|
|
DNASSERT(m_pAllPlayersGroup == NULL);
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
DNASSERT(m_NameTableArray != NULL);
|
|
DNASSERT(m_dwNameTableSize > 0);
|
|
DNASSERT(m_dwNumFreeEntries == (m_dwNameTableSize - 1));
|
|
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
|
|
m_dpnidMask = 0;
|
|
m_dwVersion = 1;
|
|
m_dwLatestVersion = 0;
|
|
m_dwConnectVersion = 0;
|
|
m_lOutstandingConnections = 0;
|
|
|
|
#pragma TODO(vanceo, "Should we bother?")
|
|
/*
|
|
#ifdef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (m_NameTableArray != NULL)
|
|
{
|
|
#endif // DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
// Re-link the FREE entries in order.
|
|
|
|
m_dwLastFreeEntry = m_dwNameTableSize - 1;
|
|
|
|
// Very last FREE entry will not wrap to 0
|
|
pNewArray[m_dwLastFreeEntry].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(0);
|
|
pNewArray[m_dwLastFreeEntry].dwFlags = 0;
|
|
|
|
for (dw = 0 ; dw < m_dwLastFreeEntry ; dw++)
|
|
{
|
|
pNewArray[dw].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(static_cast<DWORD_PTR>(dw+1));
|
|
pNewArray[dw].dwFlags = 0;
|
|
}
|
|
|
|
m_dwFirstFreeEntry = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[0].pNameTableEntry));
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::UpdateTable"
|
|
|
|
HRESULT CNameTable::UpdateTable(const DWORD dwIndex,
|
|
CNameTableEntry *const pNameTableEntry)
|
|
{
|
|
BOOL bFound;
|
|
DWORD dw;
|
|
|
|
DNASSERT(dwIndex < m_dwNameTableSize);
|
|
DNASSERT(!(m_NameTableArray[dwIndex].dwFlags & NAMETABLE_ARRAY_ENTRY_FLAG_VALID));
|
|
|
|
if (m_dwFirstFreeEntry == dwIndex)
|
|
{
|
|
m_dwFirstFreeEntry = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[m_dwFirstFreeEntry].pNameTableEntry));
|
|
bFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bFound = FALSE;
|
|
dw = m_dwFirstFreeEntry;
|
|
while (!bFound && (dw != m_dwLastFreeEntry))
|
|
{
|
|
if (m_NameTableArray[dw].pNameTableEntry == reinterpret_cast<CNameTableEntry*>(static_cast<DWORD_PTR>(dwIndex)))
|
|
{
|
|
m_NameTableArray[dw].pNameTableEntry = m_NameTableArray[dwIndex].pNameTableEntry;
|
|
if (m_dwLastFreeEntry == dwIndex)
|
|
{
|
|
m_dwLastFreeEntry = dw;
|
|
}
|
|
bFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dw = static_cast<DWORD>(reinterpret_cast<DWORD_PTR>(m_NameTableArray[dw].pNameTableEntry));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
return(DPNERR_GENERIC);
|
|
}
|
|
|
|
pNameTableEntry->AddRef();
|
|
m_NameTableArray[dwIndex].pNameTableEntry = pNameTableEntry;
|
|
m_NameTableArray[dwIndex].dwFlags |= NAMETABLE_ARRAY_ENTRY_FLAG_VALID;
|
|
|
|
//
|
|
// Insert into entry bilink
|
|
//
|
|
if (pNameTableEntry->IsGroup())
|
|
{
|
|
pNameTableEntry->m_bilinkEntries.InsertBefore(&m_bilinkGroups);
|
|
}
|
|
else
|
|
{
|
|
pNameTableEntry->m_bilinkEntries.InsertBefore(&m_bilinkPlayers);
|
|
}
|
|
|
|
DNASSERT(m_dwNumFreeEntries > 0);
|
|
m_dwNumFreeEntries--;
|
|
|
|
return(DPN_OK);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::InsertEntry"
|
|
|
|
HRESULT CNameTable::InsertEntry(CNameTableEntry *const pNameTableEntry)
|
|
{
|
|
HRESULT hResultCode;
|
|
DWORD dwIndex;
|
|
|
|
DNASSERT(pNameTableEntry != NULL);
|
|
DNASSERT(pNameTableEntry->GetDPNID() != 0);
|
|
|
|
dwIndex = DECODE_INDEX(pNameTableEntry->GetDPNID());
|
|
|
|
WriteLock();
|
|
|
|
while (dwIndex >= m_dwNameTableSize)
|
|
{
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (GrowNameTable() != DPN_OK)
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
Unlock();
|
|
DNASSERTX(! "Couldn't fit entry into nametable!", 2);
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if ((hResultCode = UpdateTable(dwIndex,pNameTableEntry)) != DPN_OK)
|
|
{
|
|
Unlock();
|
|
return(DPNERR_GENERIC);
|
|
}
|
|
|
|
Unlock();
|
|
|
|
#ifdef DBG
|
|
ValidateArray();
|
|
#endif // DBG
|
|
|
|
return(DPN_OK);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ReleaseEntry"
|
|
|
|
void CNameTable::ReleaseEntry(const DWORD dwIndex)
|
|
{
|
|
CNameTableEntry *pNTEntry;
|
|
|
|
DNASSERT(dwIndex != 0);
|
|
|
|
pNTEntry = m_NameTableArray[dwIndex].pNameTableEntry;
|
|
m_NameTableArray[dwIndex].pNameTableEntry = NULL;
|
|
pNTEntry->m_bilinkEntries.RemoveFromList();
|
|
pNTEntry->Release();
|
|
|
|
if (m_dwNumFreeEntries == 0)
|
|
{
|
|
m_dwFirstFreeEntry = dwIndex;
|
|
}
|
|
else
|
|
{
|
|
m_NameTableArray[m_dwLastFreeEntry].pNameTableEntry = reinterpret_cast<CNameTableEntry*>(static_cast<DWORD_PTR>(dwIndex));
|
|
}
|
|
m_dwLastFreeEntry = dwIndex;
|
|
m_NameTableArray[m_dwLastFreeEntry].dwFlags &= (~NAMETABLE_ARRAY_ENTRY_FLAG_VALID);
|
|
m_dwNumFreeEntries++;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::EmptyTable"
|
|
|
|
void CNameTable::EmptyTable( const HRESULT hrReason )
|
|
{
|
|
DWORD dw;
|
|
CNameTableEntry *pNTEntry;
|
|
DWORD dwGroupReason;
|
|
DWORD dwPlayerReason;
|
|
CBilink *pBilink;
|
|
CQueuedMsg *pQueuedMsg;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: hrReason [0x%lx]",hrReason);
|
|
|
|
DNASSERT( (hrReason == DPN_OK) || (hrReason == DPNERR_HOSTTERMINATEDSESSION) || (hrReason == DPNERR_CONNECTIONLOST));
|
|
|
|
if (!(m_pdnObject->dwFlags & DN_OBJECT_FLAG_CLIENT))
|
|
{
|
|
//
|
|
// Determine destruction reason to pass to application
|
|
//
|
|
switch (hrReason)
|
|
{
|
|
case DPN_OK:
|
|
{
|
|
dwPlayerReason = DPNDESTROYPLAYERREASON_NORMAL;
|
|
dwGroupReason = DPNDESTROYGROUPREASON_NORMAL;
|
|
break;
|
|
}
|
|
case DPNERR_HOSTTERMINATEDSESSION:
|
|
{
|
|
dwPlayerReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
|
|
dwGroupReason = DPNDESTROYGROUPREASON_SESSIONTERMINATED;
|
|
break;
|
|
}
|
|
case DPNERR_CONNECTIONLOST:
|
|
{
|
|
dwPlayerReason = DPNDESTROYPLAYERREASON_CONNECTIONLOST;
|
|
dwGroupReason = DPNDESTROYGROUPREASON_NORMAL;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DNASSERT( FALSE ); // Should never get here !
|
|
dwPlayerReason = DPNDESTROYPLAYERREASON_NORMAL;
|
|
dwGroupReason = DPNDESTROYGROUPREASON_NORMAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// To make VanceO happy, I've agreed to pre-mark the group destructions as NORMAL,
|
|
// rather than AUTODESTRUCT
|
|
//
|
|
ReadLock();
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
while (pBilink != &m_bilinkGroups)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( dwGroupReason );
|
|
}
|
|
pNTEntry->Unlock();
|
|
pNTEntry = NULL;
|
|
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
Unlock();
|
|
|
|
for (dw = 0 ; dw < m_dwNameTableSize ; dw++)
|
|
{
|
|
pNTEntry = NULL;
|
|
ReadLock();
|
|
if ((m_NameTableArray[dw].dwFlags & NAMETABLE_ARRAY_ENTRY_FLAG_VALID) &&
|
|
(m_NameTableArray[dw].pNameTableEntry))
|
|
{
|
|
//
|
|
// Cleanup this entry (if it's not disconnecting) and then release it
|
|
//
|
|
m_NameTableArray[dw].pNameTableEntry->Lock();
|
|
if (!m_NameTableArray[dw].pNameTableEntry->IsDisconnecting())
|
|
{
|
|
m_NameTableArray[dw].pNameTableEntry->AddRef();
|
|
pNTEntry = m_NameTableArray[dw].pNameTableEntry;
|
|
}
|
|
m_NameTableArray[dw].pNameTableEntry->Unlock();
|
|
Unlock();
|
|
|
|
if (pNTEntry)
|
|
{
|
|
//
|
|
// Set destroy reason if required
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
if (pNTEntry->IsGroup())
|
|
{
|
|
pNTEntry->SetDestroyReason( dwGroupReason );
|
|
}
|
|
else
|
|
{
|
|
pNTEntry->SetDestroyReason( dwPlayerReason );
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
//
|
|
// Delete entry
|
|
//
|
|
if (pNTEntry->IsGroup())
|
|
{
|
|
if (!pNTEntry->IsAllPlayersGroup())
|
|
{
|
|
DeleteGroup(pNTEntry->GetDPNID(),NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CConnection *pConnection;
|
|
|
|
pConnection = NULL;
|
|
|
|
pNTEntry->GetConnectionRef( &pConnection );
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
DeletePlayer(pNTEntry->GetDPNID(),NULL);
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Set reason for short-cut pointers (if required)
|
|
//
|
|
ReadLock();
|
|
if (m_pLocalPlayer)
|
|
{
|
|
m_pLocalPlayer->Lock();
|
|
if (m_pLocalPlayer->GetDestroyReason() == 0)
|
|
{
|
|
m_pLocalPlayer->SetDestroyReason( dwPlayerReason );
|
|
}
|
|
m_pLocalPlayer->Unlock();
|
|
}
|
|
if (m_pHostPlayer)
|
|
{
|
|
m_pHostPlayer->Lock();
|
|
if (m_pHostPlayer->GetDestroyReason() == 0)
|
|
{
|
|
m_pHostPlayer->SetDestroyReason( dwPlayerReason );
|
|
}
|
|
m_pHostPlayer->Unlock();
|
|
}
|
|
if (m_pAllPlayersGroup)
|
|
{
|
|
m_pAllPlayersGroup->Lock();
|
|
if (m_pAllPlayersGroup->GetDestroyReason() == 0)
|
|
{
|
|
m_pAllPlayersGroup->SetDestroyReason( dwGroupReason );
|
|
}
|
|
m_pAllPlayersGroup->Unlock();
|
|
}
|
|
Unlock();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Disconnect from Host and remove any queued messages from Host player (on Client)
|
|
//
|
|
if (GetHostPlayerRef(&pNTEntry) == DPN_OK)
|
|
{
|
|
CConnection *pConnection;
|
|
|
|
pConnection = NULL;
|
|
|
|
pNTEntry->GetConnectionRef( &pConnection );
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
|
|
pNTEntry->Lock();
|
|
pBilink = pNTEntry->m_bilinkQueuedMsgs.GetNext();
|
|
while (pBilink != &pNTEntry->m_bilinkQueuedMsgs)
|
|
{
|
|
pQueuedMsg = CONTAINING_OBJECT(pBilink,CQueuedMsg,m_bilinkQueuedMsgs);
|
|
pQueuedMsg->m_bilinkQueuedMsgs.RemoveFromList();
|
|
DEBUG_ONLY(pNTEntry->m_lNumQueuedMsgs--);
|
|
|
|
pNTEntry->Unlock();
|
|
|
|
DNASSERT(pQueuedMsg->GetAsyncOp() != NULL);
|
|
DNASSERT(pQueuedMsg->GetAsyncOp()->GetHandle() != 0);
|
|
|
|
DNEnterCriticalSection(&m_pdnObject->csActiveList);
|
|
pQueuedMsg->GetAsyncOp()->m_bilinkActiveList.RemoveFromList();
|
|
DNLeaveCriticalSection(&m_pdnObject->csActiveList);
|
|
|
|
if (SUCCEEDED(m_pdnObject->HandleTable.Destroy( pQueuedMsg->GetAsyncOp()->GetHandle(), NULL )))
|
|
{
|
|
// Release the HandleTable reference
|
|
pQueuedMsg->GetAsyncOp()->Release();
|
|
}
|
|
|
|
pQueuedMsg->GetAsyncOp()->Release();
|
|
pQueuedMsg->SetAsyncOp( NULL );
|
|
pQueuedMsg->ReturnSelfToPool();
|
|
pQueuedMsg = NULL;
|
|
|
|
pNTEntry->Lock();
|
|
|
|
pBilink = pNTEntry->m_bilinkQueuedMsgs.GetNext();
|
|
}
|
|
pNTEntry->Unlock();
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
DNASSERT(pConnection == NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove short-cut pointers
|
|
//
|
|
ClearLocalPlayer();
|
|
ClearHostPlayer();
|
|
ClearAllPlayersGroup();
|
|
|
|
DPFX(DPFPREP, 6,"Returning");
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::FindEntry"
|
|
|
|
HRESULT CNameTable::FindEntry(const DPNID dpnid,
|
|
CNameTableEntry **const ppNameTableEntry)
|
|
{
|
|
DWORD dwIndex;
|
|
HRESULT hResultCode;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%lx], ppNameTableEntry [0x%p]",dpnid,ppNameTableEntry);
|
|
|
|
DNASSERT(ppNameTableEntry != NULL);
|
|
|
|
if (dpnid == 0)
|
|
{
|
|
hResultCode = DPNERR_DOESNOTEXIST;
|
|
goto Failure;
|
|
}
|
|
|
|
ReadLock();
|
|
dwIndex = DECODE_INDEX(dpnid);
|
|
if ((dwIndex >= m_dwNameTableSize)
|
|
|| !(m_NameTableArray[dwIndex].dwFlags & NAMETABLE_ARRAY_ENTRY_FLAG_VALID)
|
|
|| (m_NameTableArray[dwIndex].pNameTableEntry == NULL))
|
|
{
|
|
Unlock();
|
|
hResultCode = DPNERR_DOESNOTEXIST;
|
|
goto Failure;
|
|
}
|
|
|
|
if (!VERIFY_VERSION(dpnid,m_NameTableArray[dwIndex].pNameTableEntry->GetVersion()))
|
|
{
|
|
Unlock();
|
|
hResultCode = DPNERR_DOESNOTEXIST;
|
|
goto Failure;
|
|
}
|
|
|
|
m_NameTableArray[dwIndex].pNameTableEntry->AddRef();
|
|
*ppNameTableEntry = m_NameTableArray[dwIndex].pNameTableEntry;
|
|
|
|
Unlock();
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"hResultCode: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::FindDeletedEntry"
|
|
|
|
HRESULT CNameTable::FindDeletedEntry(const DPNID dpnid,
|
|
CNameTableEntry **const ppNTEntry)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%lx], ppNTEntry [0x%p]",dpnid,ppNTEntry);
|
|
|
|
DNASSERT(ppNTEntry != NULL);
|
|
|
|
pNTEntry = NULL;
|
|
hResultCode = DPNERR_DOESNOTEXIST;
|
|
|
|
ReadLock();
|
|
pBilink = m_bilinkDeleted.GetNext();
|
|
while (pBilink != &m_bilinkDeleted)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkDeleted);
|
|
if (pNTEntry->GetDPNID() == dpnid)
|
|
{
|
|
pNTEntry->AddRef();
|
|
hResultCode = DPN_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pBilink = pBilink->GetNext();
|
|
pNTEntry = NULL;
|
|
}
|
|
}
|
|
Unlock();
|
|
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->AddRef();
|
|
*ppNTEntry = pNTEntry;
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::AddEntry"
|
|
|
|
HRESULT CNameTable::AddEntry(CNameTableEntry *const pNTEntry)
|
|
{
|
|
DPNID dpnid;
|
|
DWORD dwIndex;
|
|
DWORD dwVersion;
|
|
HRESULT hResultCode;
|
|
|
|
WriteLock();
|
|
|
|
//
|
|
// Create DPNID
|
|
//
|
|
|
|
while (m_dwNumFreeEntries == 0)
|
|
{
|
|
#ifndef DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
if (GrowNameTable() != DPN_OK)
|
|
#endif // ! DPNBUILD_PREALLOCATEDMEMORYMODEL
|
|
{
|
|
DNASSERTX(! "No free slots in name table!", 2);
|
|
Unlock();
|
|
return(DPNERR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
DNASSERT(m_dwFirstFreeEntry != 0);
|
|
dwIndex = m_dwFirstFreeEntry;
|
|
|
|
dwVersion = ++m_dwVersion;
|
|
DPFX(DPFPREP, 8,"Setting new version [%ld]",m_dwVersion);
|
|
|
|
dpnid = CONSTRUCT_DPNID(dwIndex,dwVersion);
|
|
DNASSERT(dpnid != 0);
|
|
|
|
pNTEntry->Lock();
|
|
pNTEntry->SetDPNID(dpnid);
|
|
pNTEntry->SetVersion(dwVersion);
|
|
pNTEntry->Unlock();
|
|
|
|
dwIndex = DECODE_INDEX(dpnid);
|
|
hResultCode = UpdateTable(dwIndex,pNTEntry);
|
|
|
|
Unlock();
|
|
|
|
#ifdef DBG
|
|
ValidateArray();
|
|
#endif // DBG
|
|
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::DeletePlayer"
|
|
|
|
HRESULT CNameTable::DeletePlayer(const DPNID dpnid,
|
|
DWORD *const pdwVersion)
|
|
{
|
|
HRESULT hResultCode;
|
|
CNameTableEntry *pNTEntry;
|
|
BOOL fNotifyRelease;
|
|
BOOL fDecConnections;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%lx], pdwVersion [0x%p]",dpnid,pdwVersion);
|
|
|
|
pNTEntry = NULL;
|
|
fNotifyRelease = FALSE;
|
|
fDecConnections = FALSE;
|
|
|
|
if ((hResultCode = FindEntry(dpnid,&pNTEntry)) != DPN_OK)
|
|
{
|
|
DPFERR("Player not in NameTable");
|
|
DisplayDNError(0,hResultCode);
|
|
|
|
//
|
|
// If a version is requested, we will give one back. This might be a host migration case
|
|
// in which case even though the player was removed from the NameTable, we will want to
|
|
// send out a DESTROY_PLAYER message with an updated version number
|
|
//
|
|
if (pdwVersion)
|
|
{
|
|
if (*pdwVersion == 0)
|
|
{
|
|
WriteLock();
|
|
*pdwVersion = ++m_dwVersion;
|
|
DPFX(DPFPREP, 8,"Setting new version [%ld]",m_dwVersion);
|
|
Unlock();
|
|
}
|
|
}
|
|
goto Failure;
|
|
}
|
|
DNASSERT(!pNTEntry->IsGroup());
|
|
|
|
//
|
|
// Don't do anything if already disconnecting.
|
|
// Otherwise, set disconnecting to prevent others from cleaning up, and clean up
|
|
//
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting())
|
|
{
|
|
pNTEntry->StartDisconnecting();
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
}
|
|
if ((pNTEntry->IsCreated() || pNTEntry->IsIndicated() || pNTEntry->IsInUse()) && !pNTEntry->IsNeedToDestroy())
|
|
{
|
|
pNTEntry->SetNeedToDestroy();
|
|
fNotifyRelease = TRUE;
|
|
}
|
|
if ( !pNTEntry->IsCreated()
|
|
&& pNTEntry->IsConnecting()
|
|
&& (m_pdnObject->dwFlags & (DN_OBJECT_FLAG_CONNECTING | DN_OBJECT_FLAG_CONNECTED))
|
|
&& (pNTEntry->GetVersion() <= m_dwConnectVersion))
|
|
{
|
|
fDecConnections = TRUE;
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
//
|
|
// Remove this player from any groups they belong to
|
|
//
|
|
RemoveAllGroupsFromPlayer( pNTEntry );
|
|
|
|
//
|
|
// Autodestruct any groups this player owns (will also remove any players from those groups first)
|
|
//
|
|
if (pNTEntry->GetDPNID() != 0)
|
|
{
|
|
AutoDestructGroups( pNTEntry->GetDPNID() );
|
|
}
|
|
|
|
if (fNotifyRelease)
|
|
{
|
|
pNTEntry->NotifyRelease();
|
|
}
|
|
|
|
//
|
|
// Adjust player count
|
|
//
|
|
m_pdnObject->ApplicationDesc.DecPlayerCount();
|
|
if (fDecConnections)
|
|
{
|
|
DecOutstandingConnections();
|
|
}
|
|
|
|
//
|
|
// Update version and remove from NameTable
|
|
//
|
|
WriteLock();
|
|
pNTEntry->Lock();
|
|
if ((pNTEntry->IsCreated() || pNTEntry->IsInUse()) && pNTEntry->IsNeedToDestroy())
|
|
{
|
|
//
|
|
// The DESTROY_PLAYER message has not been posted, so we will add this entry to our "deleted" list
|
|
// so that some future operations (get info,context,etc.) may succeed. This entry will be removed
|
|
// from the list then the DESTROY_PLAYER notification is posted
|
|
//
|
|
pNTEntry->m_bilinkDeleted.InsertBefore(&m_bilinkDeleted);
|
|
|
|
pNTEntry->Unlock();
|
|
|
|
ReleaseEntry(DECODE_INDEX(dpnid));
|
|
|
|
//
|
|
// Update version
|
|
//
|
|
if (pdwVersion)
|
|
{
|
|
if (*pdwVersion)
|
|
{
|
|
m_dwVersion = *pdwVersion;
|
|
}
|
|
else
|
|
{
|
|
*pdwVersion = ++m_dwVersion;
|
|
}
|
|
DPFX(DPFPREP, 8,"Setting new version [%ld]",m_dwVersion);
|
|
}
|
|
Unlock();
|
|
}
|
|
else
|
|
{
|
|
CBilink *pBilink;
|
|
CQueuedMsg *pQueuedMsg;
|
|
|
|
//
|
|
// Remove any queued messages at this stage. A CREATE_PLAYER won't be generated, so no messages
|
|
// will be passed up.
|
|
//
|
|
// This is probably wrong since for reliable traffic, we assume it got here
|
|
//
|
|
Unlock();
|
|
|
|
#pragma BUGBUG(minara,"This is probably wrong since reliable traffic should be indicated rather than just thrown away!")
|
|
pBilink = pNTEntry->m_bilinkQueuedMsgs.GetNext();
|
|
while (pBilink != &pNTEntry->m_bilinkQueuedMsgs)
|
|
{
|
|
pQueuedMsg = CONTAINING_OBJECT(pBilink,CQueuedMsg,m_bilinkQueuedMsgs);
|
|
pQueuedMsg->m_bilinkQueuedMsgs.RemoveFromList();
|
|
DEBUG_ONLY(pNTEntry->m_lNumQueuedMsgs--);
|
|
|
|
pNTEntry->Unlock();
|
|
|
|
DNASSERT(pQueuedMsg->GetAsyncOp() != NULL);
|
|
DNASSERT(pQueuedMsg->GetAsyncOp()->GetHandle() != 0);
|
|
|
|
DNDoCancelCommand( m_pdnObject,pQueuedMsg->GetAsyncOp() );
|
|
|
|
pQueuedMsg->GetAsyncOp()->Release();
|
|
pQueuedMsg->SetAsyncOp( NULL );
|
|
pQueuedMsg->ReturnSelfToPool();
|
|
pQueuedMsg = NULL;
|
|
|
|
pNTEntry->Lock();
|
|
|
|
pBilink = pNTEntry->m_bilinkQueuedMsgs.GetNext();
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
//
|
|
// Update version
|
|
//
|
|
WriteLock();
|
|
ReleaseEntry(DECODE_INDEX(dpnid));
|
|
if (pdwVersion)
|
|
{
|
|
if (*pdwVersion)
|
|
{
|
|
m_dwVersion = *pdwVersion;
|
|
}
|
|
else
|
|
{
|
|
*pdwVersion = ++m_dwVersion;
|
|
}
|
|
DPFX(DPFPREP, 8,"Setting new version [%ld]",m_dwVersion);
|
|
}
|
|
Unlock();
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
pNTEntry->Unlock();
|
|
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::DeleteGroup"
|
|
|
|
HRESULT CNameTable::DeleteGroup(const DPNID dpnid,
|
|
DWORD *const pdwVersion)
|
|
{
|
|
HRESULT hResultCode;
|
|
CNameTableEntry *pNTEntry;
|
|
BOOL fNotifyRelease;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: dpnid [0x%lx], pdwVersion [0x%p]",dpnid,pdwVersion);
|
|
|
|
pNTEntry = NULL;
|
|
fNotifyRelease = FALSE;
|
|
|
|
if ((hResultCode = FindEntry(dpnid,&pNTEntry)) != DPN_OK)
|
|
{
|
|
DPFERR("Player not in NameTable");
|
|
DisplayDNError(0,hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
DNASSERT(pNTEntry->IsGroup() && !pNTEntry->IsAllPlayersGroup());
|
|
|
|
//
|
|
// Don't do anything if already disconnecting.
|
|
// Otherwise, set disconnecting to prevent others from cleaning up, and clean up
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
//
|
|
// Default this if it isn't set
|
|
//
|
|
pNTEntry->SetDestroyReason( DPNDESTROYGROUPREASON_NORMAL );
|
|
}
|
|
if (!pNTEntry->IsDisconnecting())
|
|
{
|
|
pNTEntry->StartDisconnecting();
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
}
|
|
if (pNTEntry->IsCreated() && !pNTEntry->IsNeedToDestroy())
|
|
{
|
|
pNTEntry->SetNeedToDestroy();
|
|
fNotifyRelease = TRUE;
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
RemoveAllPlayersFromGroup( pNTEntry );
|
|
|
|
if (fNotifyRelease)
|
|
{
|
|
pNTEntry->NotifyRelease();
|
|
}
|
|
|
|
//
|
|
// Update version and remove from NameTable
|
|
//
|
|
WriteLock();
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->IsNeedToDestroy())
|
|
{
|
|
//
|
|
// The DESTROY_GROUP message has not been posted, so we will add this entry to our "deleted" list
|
|
// so that some future operations (get info,context,etc.) may succeed. This entry will be removed
|
|
// from the list then the DESTROY_GROUP notification is posted
|
|
//
|
|
pNTEntry->m_bilinkDeleted.InsertBefore(&m_bilinkDeleted);
|
|
}
|
|
pNTEntry->Unlock();
|
|
ReleaseEntry(DECODE_INDEX(dpnid));
|
|
if (pdwVersion)
|
|
{
|
|
if (*pdwVersion)
|
|
{
|
|
m_dwVersion = *pdwVersion;
|
|
}
|
|
else
|
|
{
|
|
*pdwVersion = ++m_dwVersion;
|
|
}
|
|
DPFX(DPFPREP, 8,"Setting new version [%ld]",m_dwVersion);
|
|
}
|
|
Unlock();
|
|
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
pNTEntry->Unlock();
|
|
|
|
hResultCode = DPNERR_INVALIDGROUP;
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::AddPlayerToGroup"
|
|
|
|
HRESULT CNameTable::AddPlayerToGroup(CNameTableEntry *const pGroup,
|
|
CNameTableEntry *const pPlayer,
|
|
DWORD *const pdwVersion)
|
|
{
|
|
HRESULT hResultCode;
|
|
CGroupMember *pGroupMember;
|
|
CGroupConnection *pGroupConnection;
|
|
CConnection *pConnection;
|
|
BOOL fNotifyAdd;
|
|
BOOL fRemove;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pGroup [0x%p], pPlayer [0x%p], pdwVersion [0x%p]",pGroup,pPlayer,pdwVersion);
|
|
|
|
DNASSERT(pGroup != NULL);
|
|
DNASSERT(pPlayer != NULL);
|
|
|
|
pGroupConnection = NULL;
|
|
pGroupMember = NULL;
|
|
pConnection = NULL;
|
|
|
|
if (!pGroup->IsGroup())
|
|
{
|
|
hResultCode = DPNERR_INVALIDGROUP;
|
|
goto Failure;
|
|
}
|
|
if (pPlayer->IsGroup())
|
|
{
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Create the group connection
|
|
//
|
|
if ((hResultCode = GroupConnectionNew(m_pdnObject,&pGroupConnection)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not allocate name table group connection entry from FPM");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
pGroupConnection->SetGroup( pGroup );
|
|
|
|
//
|
|
// Create new group membership record
|
|
//
|
|
if ((hResultCode = GroupMemberNew(m_pdnObject,&pGroupMember)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get new group member");
|
|
DisplayDNError(0,hResultCode);
|
|
DNASSERT(FALSE);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Set group connection on group membership record
|
|
//
|
|
pGroupMember->SetGroupConnection(pGroupConnection);
|
|
|
|
//
|
|
// Add player to group
|
|
//
|
|
fNotifyAdd = FALSE;
|
|
fRemove = FALSE;
|
|
WriteLock();
|
|
pGroup->Lock();
|
|
pPlayer->Lock();
|
|
pGroupMember->Lock();
|
|
if (!pGroup->IsDisconnecting() && !pPlayer->IsDisconnecting())
|
|
{
|
|
pGroupMember->MakeValid();
|
|
pGroupMember->GetGroupConnection()->MakeValid();
|
|
|
|
//
|
|
// Set group membership (checks for duplicates as well)
|
|
//
|
|
if ((hResultCode = pGroupMember->SetMembership(pGroup,pPlayer,pdwVersion)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not set membership record");
|
|
DisplayDNError(0,hResultCode);
|
|
Unlock();
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
pGroupMember->Unlock();
|
|
goto Failure;
|
|
}
|
|
//
|
|
// Generate notification (ALL_PLAYERS GROUP should never be "Created")
|
|
//
|
|
if (pGroup->IsCreated() && pPlayer->IsCreated())
|
|
{
|
|
//
|
|
// Add the player's connection to the group connection record
|
|
//
|
|
if (pPlayer->GetConnection() != NULL)
|
|
{
|
|
pGroupConnection->SetConnection( pPlayer->GetConnection() );
|
|
}
|
|
|
|
if (!pGroupMember->IsNeedToAdd() && !pGroupMember->IsAvailable() && pGroupMember->GetGroupConnection()->IsConnected())
|
|
{
|
|
pGroupMember->SetNeedToAdd();
|
|
fNotifyAdd = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Need to set up the group connection if this is the ALL_PLAYERS group
|
|
//
|
|
if (pGroup->IsAllPlayersGroup())
|
|
{
|
|
if (pPlayer->GetConnection() != NULL)
|
|
{
|
|
pGroupConnection->SetConnection( pPlayer->GetConnection() );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prevent a DESTROY_PLAYER/DESTROY_GROUP from occurring until this GroupMember record is cleared
|
|
//
|
|
pGroup->NotifyAddRef();
|
|
pPlayer->NotifyAddRef();
|
|
}
|
|
Unlock();
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
pGroupMember->Unlock();
|
|
|
|
if (fNotifyAdd)
|
|
{
|
|
DNUserAddPlayerToGroup(m_pdnObject,pGroup,pPlayer);
|
|
|
|
pGroupMember->Lock();
|
|
pGroupMember->ClearNeedToAdd();
|
|
pGroupMember->MakeAvailable();
|
|
if (pGroupMember->IsNeedToRemove())
|
|
{
|
|
fRemove = TRUE;
|
|
}
|
|
pGroupMember->Unlock();
|
|
}
|
|
if (fRemove)
|
|
{
|
|
RemovePlayerFromGroup(pGroup,pPlayer,NULL);
|
|
}
|
|
|
|
pGroupConnection->Release();
|
|
pGroupConnection = NULL;
|
|
|
|
pGroupMember->Release();
|
|
pGroupMember = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pGroupConnection)
|
|
{
|
|
pGroupConnection->Release();
|
|
pGroupConnection = NULL;
|
|
}
|
|
if (pGroupMember)
|
|
{
|
|
pGroupMember->Release();
|
|
pGroupMember = NULL;
|
|
}
|
|
if (pConnection)
|
|
{
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::RemovePlayerFromGroup"
|
|
|
|
HRESULT CNameTable::RemovePlayerFromGroup(CNameTableEntry *const pGroup,
|
|
CNameTableEntry *const pPlayer,
|
|
DWORD *const pdwVersion)
|
|
{
|
|
CGroupMember *pGroupMember;
|
|
CBilink *pBilink;
|
|
BOOL fNotifyRemove;
|
|
HRESULT hResultCode;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pGroup [0x%p], pPlayer [0x%p], pdwVersion [0x%p]",pGroup,pPlayer,pdwVersion);
|
|
|
|
DNASSERT(pGroup != NULL);
|
|
DNASSERT(pPlayer != NULL);
|
|
|
|
pGroupMember = NULL;
|
|
fNotifyRemove = FALSE;
|
|
|
|
WriteLock();
|
|
pGroup->Lock();
|
|
pPlayer->Lock();
|
|
|
|
//
|
|
// The first order of business is to locate the GroupMembership record.
|
|
// We will use the player's NameTable entry and scan through the
|
|
// group membership bilink until we find the required entry.
|
|
// (We're assuming that this will be faster than going the other route.)
|
|
//
|
|
pBilink = pPlayer->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pPlayer->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkGroups);
|
|
if (pGroupMember->GetGroup() == pGroup)
|
|
{
|
|
pGroupMember->AddRef();
|
|
break;
|
|
}
|
|
pGroupMember = NULL;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
if (pGroupMember == NULL)
|
|
{
|
|
Unlock();
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
hResultCode = DPNERR_PLAYERNOTINGROUP;
|
|
goto Failure;
|
|
}
|
|
|
|
DNASSERT(pGroupMember != NULL);
|
|
pGroupMember->Lock();
|
|
|
|
//
|
|
// Ensure no one else is trying to remove this already
|
|
//
|
|
if (!pGroupMember->IsValid() || pGroupMember->IsNeedToRemove())
|
|
{
|
|
Unlock();
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
pGroupMember->Unlock();
|
|
hResultCode = DPNERR_PLAYERNOTINGROUP;
|
|
goto Failure;
|
|
}
|
|
pGroupMember->SetNeedToRemove();
|
|
|
|
//
|
|
// We will only notify the application if the player is not being added to a group
|
|
//
|
|
if (!pGroupMember->IsNeedToAdd())
|
|
{
|
|
//
|
|
// Either this is already indicated, or is not about to be indicated, so remove it
|
|
// (and see if we need to generate a notification)
|
|
//
|
|
pGroupMember->RemoveMembership( pdwVersion );
|
|
|
|
if (pGroupMember->IsAvailable())
|
|
{
|
|
pGroupMember->MakeUnavailable();
|
|
if (!pGroup->IsAllPlayersGroup())
|
|
{
|
|
fNotifyRemove = TRUE;
|
|
}
|
|
}
|
|
}
|
|
Unlock();
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
pGroupMember->Unlock();
|
|
|
|
if (fNotifyRemove)
|
|
{
|
|
DNUserRemovePlayerFromGroup(m_pdnObject,pGroup,pPlayer);
|
|
}
|
|
|
|
//
|
|
// Trigger a DESTROY_PLAYER/DESTROY_GROUP if this was the last member
|
|
//
|
|
pGroup->NotifyRelease();
|
|
pPlayer->NotifyRelease();
|
|
|
|
pGroupMember->Release();
|
|
pGroupMember = NULL;
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pGroupMember)
|
|
{
|
|
pGroupMember->Release();
|
|
pGroupMember = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::RemoveAllPlayersFromGroup"
|
|
|
|
HRESULT CNameTable::RemoveAllPlayersFromGroup(CNameTableEntry *const pGroup)
|
|
{
|
|
CNameTableEntry **PlayerList;
|
|
CBilink *pBilink;
|
|
HRESULT hResultCode;
|
|
DWORD dwCount;
|
|
DWORD dwActual;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pGroup [0x%p]",pGroup);
|
|
|
|
DNASSERT(pGroup != NULL);
|
|
|
|
PlayerList = NULL;
|
|
|
|
//
|
|
// This is not an elegant solution - we will build a list of membership records and remove each one
|
|
//
|
|
dwCount = 0;
|
|
dwActual = 0;
|
|
pGroup->Lock();
|
|
DNASSERT(pGroup->IsDisconnecting());
|
|
pBilink = pGroup->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pGroup->m_bilinkMembership)
|
|
{
|
|
dwCount++;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
if (dwCount)
|
|
{
|
|
CGroupMember *pGroupMember;
|
|
|
|
pGroupMember = NULL;
|
|
|
|
if ((PlayerList = static_cast<CNameTableEntry**>(MemoryBlockAlloc(m_pdnObject,dwCount*sizeof(CNameTableEntry*)))) == NULL)
|
|
{
|
|
DPFERR("Could not allocate player list");
|
|
hResultCode = DPNERR_OUTOFMEMORY;
|
|
DNASSERT(FALSE);
|
|
pGroup->Unlock();
|
|
goto Failure;
|
|
}
|
|
pBilink = pGroup->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pGroup->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkPlayers);
|
|
|
|
pGroupMember->Lock();
|
|
if (pGroupMember->IsValid() && !pGroupMember->IsNeedToRemove() && pGroupMember->GetPlayer())
|
|
{
|
|
DNASSERT(dwActual < dwCount);
|
|
pGroupMember->GetPlayer()->AddRef();
|
|
PlayerList[dwActual] = pGroupMember->GetPlayer();
|
|
dwActual++;
|
|
}
|
|
pGroupMember->Unlock();
|
|
|
|
pBilink = pBilink->GetNext();
|
|
pGroupMember = NULL;
|
|
}
|
|
|
|
DNASSERT(pGroupMember == NULL);
|
|
}
|
|
pGroup->Unlock();
|
|
|
|
if (PlayerList)
|
|
{
|
|
DWORD dw;
|
|
|
|
for (dw = 0 ; dw < dwActual ; dw++)
|
|
{
|
|
DNASSERT(PlayerList[dw] != NULL);
|
|
|
|
RemovePlayerFromGroup(pGroup,PlayerList[dw],NULL);
|
|
PlayerList[dw]->Release();
|
|
PlayerList[dw] = NULL;
|
|
}
|
|
|
|
MemoryBlockFree(m_pdnObject,PlayerList);
|
|
PlayerList = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (PlayerList)
|
|
{
|
|
MemoryBlockFree(m_pdnObject,PlayerList);
|
|
PlayerList = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::RemoveAllGroupsFromPlayer"
|
|
|
|
HRESULT CNameTable::RemoveAllGroupsFromPlayer(CNameTableEntry *const pPlayer)
|
|
{
|
|
CNameTableEntry *apGroupList[32];
|
|
CBilink *pBilink;
|
|
HRESULT hResultCode;
|
|
CGroupMember *pGroupMember;
|
|
DWORD dwRemainingCount;
|
|
DWORD dwCurrentCount;
|
|
#ifdef DBG
|
|
DWORD dwInitialCount;
|
|
#endif // DBG
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pPlayer [0x%p]",pPlayer);
|
|
|
|
DNASSERT(pPlayer != NULL);
|
|
|
|
memset(apGroupList, 0, sizeof(apGroupList));
|
|
pGroupMember = NULL;
|
|
|
|
//
|
|
// This is not an elegant solution - we will build a list of membership records and remove each one
|
|
//
|
|
dwRemainingCount = 0;
|
|
pPlayer->Lock();
|
|
DNASSERT(pPlayer->IsDisconnecting());
|
|
pBilink = pPlayer->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pPlayer->m_bilinkMembership)
|
|
{
|
|
dwRemainingCount++;
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
pPlayer->Unlock();
|
|
|
|
#ifdef DBG
|
|
dwInitialCount = dwRemainingCount;
|
|
#endif // DBG
|
|
|
|
while (dwRemainingCount > 0)
|
|
{
|
|
dwRemainingCount = 0;
|
|
dwCurrentCount = 0;
|
|
pPlayer->Lock();
|
|
pBilink = pPlayer->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pPlayer->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkGroups);
|
|
|
|
pGroupMember->Lock();
|
|
if (pGroupMember->IsValid() && !pGroupMember->IsNeedToRemove() && pGroupMember->GetGroup())
|
|
{
|
|
if (dwCurrentCount < (sizeof(apGroupList) / sizeof(CNameTableEntry*)))
|
|
{
|
|
pGroupMember->GetGroup()->AddRef();
|
|
apGroupList[dwCurrentCount] = pGroupMember->GetGroup();
|
|
dwCurrentCount++;
|
|
#ifdef DBG
|
|
DNASSERT(dwCurrentCount <= dwInitialCount);
|
|
#endif // DBG
|
|
}
|
|
else
|
|
{
|
|
dwRemainingCount++;
|
|
|
|
//
|
|
// The list should never grow. In fact it should
|
|
// always be smaller because the current group list
|
|
// should have taken some.
|
|
//
|
|
#ifdef DBG
|
|
DNASSERT(dwRemainingCount < dwInitialCount);
|
|
#endif // DBG
|
|
}
|
|
}
|
|
pGroupMember->Unlock();
|
|
|
|
pBilink = pBilink->GetNext();
|
|
pGroupMember = NULL;
|
|
}
|
|
|
|
DNASSERT(pGroupMember == NULL);
|
|
|
|
pPlayer->Unlock();
|
|
|
|
if (dwCurrentCount > 0)
|
|
{
|
|
DWORD dw;
|
|
|
|
for (dw = 0 ; dw < dwCurrentCount ; dw++)
|
|
{
|
|
DNASSERT(apGroupList[dw] != NULL);
|
|
|
|
RemovePlayerFromGroup(apGroupList[dw],pPlayer,NULL);
|
|
apGroupList[dw]->Release();
|
|
apGroupList[dw] = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(dwRemainingCount == 0);
|
|
}
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::IsMember"
|
|
|
|
BOOL CNameTable::IsMember(const DPNID dpnidGroup,
|
|
const DPNID dpnidPlayer)
|
|
{
|
|
CNameTableEntry *pNTEntry;
|
|
CGroupMember *pGroupMember;
|
|
CBilink *pBilink;
|
|
BOOL bFound;
|
|
|
|
bFound = FALSE;
|
|
|
|
if (FindEntry(dpnidGroup,&pNTEntry) != DPN_OK)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Is this a group ?
|
|
//
|
|
if (!pNTEntry->IsGroup())
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
pNTEntry->Lock();
|
|
pBilink = pNTEntry->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pNTEntry->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkPlayers);
|
|
if (pGroupMember->GetPlayer()->GetDPNID() == dpnidPlayer)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
pNTEntry->Unlock();
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
Exit:
|
|
return(bFound);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::PackNameTable"
|
|
|
|
HRESULT CNameTable::PackNameTable(CNameTableEntry *const pTarget,
|
|
CPackedBuffer *const pPackedBuffer)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CBilink *pBilinkMembership;
|
|
CNameTableEntry *pNTEntry;
|
|
CGroupMember *pGroupMember;
|
|
DN_NAMETABLE_INFO *pdnNTInfo;
|
|
BOOL bOutOfSpace;
|
|
DWORD dwEntryCount;
|
|
DWORD dwMembershipCount;
|
|
DWORD dwVersion;
|
|
|
|
DNASSERT(pTarget != NULL);
|
|
DNASSERT(pPackedBuffer != NULL);
|
|
|
|
//
|
|
// PackedNameTable:
|
|
// <DN_NAMETABLE_INFO>
|
|
// <DN_NAMETABLE_ENTRY_INFO> (DN_NAMETABLE_INFO.dwEntryCount entries)
|
|
// <DN_MEMBERSHIP_INFO> (DN_NAMETABLE_INFO.dwMembershipCount entries)
|
|
// ...
|
|
// <strings>
|
|
//
|
|
|
|
//
|
|
// NameTable Info
|
|
//
|
|
dwVersion = pTarget->GetVersion();
|
|
bOutOfSpace = FALSE;
|
|
pdnNTInfo = static_cast<DN_NAMETABLE_INFO*>(pPackedBuffer->GetHeadAddress());
|
|
if ((hResultCode = pPackedBuffer->AddToFront(NULL,sizeof(DN_NAMETABLE_INFO))) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
|
|
ReadLock();
|
|
|
|
//
|
|
// NameTableEntry Info
|
|
//
|
|
if (m_pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
|
|
{
|
|
dwEntryCount = 0;
|
|
|
|
//
|
|
// Players
|
|
//
|
|
pBilink = m_bilinkPlayers.GetNext();
|
|
while (pBilink != &m_bilinkPlayers)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
if (pNTEntry->GetVersion() <= dwVersion)
|
|
{
|
|
if ((hResultCode = pNTEntry->PackEntryInfo(pPackedBuffer)) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
dwEntryCount++;
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
|
|
//
|
|
// Groups
|
|
//
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
while (pBilink != &m_bilinkGroups)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
if (pNTEntry->GetVersion() <= dwVersion)
|
|
{
|
|
if ((hResultCode = pNTEntry->PackEntryInfo(pPackedBuffer)) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
dwEntryCount++;
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DNASSERT(m_pLocalPlayer != NULL);
|
|
|
|
if ((hResultCode = m_pLocalPlayer->PackEntryInfo(pPackedBuffer)) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
if ((hResultCode = pTarget->PackEntryInfo(pPackedBuffer)) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
dwEntryCount = 2;
|
|
}
|
|
|
|
//
|
|
// GroupMember Info
|
|
//
|
|
dwMembershipCount = 0;
|
|
if (m_pdnObject->dwFlags & DN_OBJECT_FLAG_PEER)
|
|
{
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
while (pBilink != &m_bilinkGroups)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
DNASSERT(pNTEntry->IsGroup());
|
|
if (!pNTEntry->IsAllPlayersGroup())
|
|
{
|
|
pBilinkMembership = pNTEntry->m_bilinkMembership.GetNext();
|
|
while (pBilinkMembership != &pNTEntry->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilinkMembership,CGroupMember,m_bilinkPlayers);
|
|
if (pGroupMember->GetVersion() <= dwVersion)
|
|
{
|
|
if ((hResultCode = pGroupMember->PackMembershipInfo(pPackedBuffer)) != DPN_OK)
|
|
{
|
|
bOutOfSpace = TRUE;
|
|
}
|
|
dwMembershipCount++;
|
|
}
|
|
pBilinkMembership = pBilinkMembership->GetNext();
|
|
}
|
|
}
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if (!bOutOfSpace)
|
|
{
|
|
pdnNTInfo->dpnid = pTarget->GetDPNID();
|
|
pdnNTInfo->dwVersion = dwVersion;
|
|
pdnNTInfo->dwVersionNotUsed = 0;
|
|
pdnNTInfo->dwEntryCount = dwEntryCount;
|
|
pdnNTInfo->dwMembershipCount = dwMembershipCount;
|
|
}
|
|
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::UnpackNameTableInfo"
|
|
|
|
HRESULT CNameTable::UnpackNameTableInfo(UNALIGNED DN_NAMETABLE_INFO *const pdnNTInfo,
|
|
BYTE *const pBufferStart,
|
|
DPNID *const pdpnid)
|
|
{
|
|
HRESULT hResultCode;
|
|
DWORD dwCount;
|
|
CNameTableEntry *pNTEntry;
|
|
UNALIGNED DN_NAMETABLE_ENTRY_INFO *pdnEntryInfo;
|
|
UNALIGNED DN_NAMETABLE_MEMBERSHIP_INFO *pdnMembershipInfo;
|
|
|
|
DNASSERT(pdnNTInfo != NULL);
|
|
DNASSERT(pBufferStart != NULL);
|
|
|
|
//
|
|
// Preset outstanding connections
|
|
//
|
|
m_lOutstandingConnections = 0;
|
|
|
|
//
|
|
// NameTable Entries
|
|
//
|
|
pdnEntryInfo = reinterpret_cast<DN_NAMETABLE_ENTRY_INFO*>(pdnNTInfo+1);
|
|
for (dwCount = 0 ; dwCount < pdnNTInfo->dwEntryCount ; dwCount++)
|
|
{
|
|
if ((hResultCode = NameTableEntryNew(m_pdnObject,&pNTEntry)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get new NameTableEntry");
|
|
DNASSERT(FALSE);
|
|
return(hResultCode);
|
|
}
|
|
|
|
if ((hResultCode = pNTEntry->UnpackEntryInfo(pdnEntryInfo,pBufferStart)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not unpack NameTableEntryInfo");
|
|
DNASSERT(FALSE);
|
|
pNTEntry->Release();
|
|
return(hResultCode);
|
|
}
|
|
|
|
//
|
|
// Increment outstanding connection count
|
|
//
|
|
if (!pNTEntry->IsGroup() && (pNTEntry->GetVersion() <= pdnNTInfo->dwVersion))
|
|
{
|
|
pNTEntry->StartConnecting(); // This will be cleared when the player has connected or disconnected
|
|
IncOutstandingConnections();
|
|
}
|
|
|
|
// Only put in NameTable if Host player
|
|
#ifndef DPNBUILD_NOSERVER
|
|
if (m_pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER | DN_OBJECT_FLAG_SERVER))
|
|
#else
|
|
if (m_pdnObject->dwFlags & (DN_OBJECT_FLAG_PEER))
|
|
#endif // DPNBUILD_NOSERVER
|
|
{
|
|
if ((hResultCode = InsertEntry(pNTEntry)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not add NameTableEntry to NameTable");
|
|
DNASSERT(FALSE);
|
|
pNTEntry->Release();
|
|
return(hResultCode);
|
|
}
|
|
}
|
|
|
|
// Check for ShortCut pointers
|
|
if (pNTEntry->GetDPNID() == pdnNTInfo->dpnid)
|
|
{
|
|
MakeLocalPlayer(pNTEntry);
|
|
}
|
|
else if (pNTEntry->IsHost())
|
|
{
|
|
MakeHostPlayer(pNTEntry);
|
|
}
|
|
else if (pNTEntry->IsAllPlayersGroup())
|
|
{
|
|
MakeAllPlayersGroup(pNTEntry);
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
pdnEntryInfo++;
|
|
}
|
|
|
|
//
|
|
// Pass back local player's DPNID
|
|
//
|
|
if (pdpnid)
|
|
{
|
|
*pdpnid = pdnNTInfo->dpnid;
|
|
}
|
|
|
|
//
|
|
// Group Memberships
|
|
//
|
|
pdnMembershipInfo = reinterpret_cast<DN_NAMETABLE_MEMBERSHIP_INFO*>(pdnEntryInfo);
|
|
for (dwCount = 0 ; dwCount < pdnNTInfo->dwMembershipCount ; dwCount++)
|
|
{
|
|
CNameTableEntry *pGroup;
|
|
|
|
pGroup = NULL;
|
|
|
|
if ((hResultCode = m_pdnObject->NameTable.FindEntry(pdnMembershipInfo->dpnidGroup,&pGroup)) == DPN_OK)
|
|
{
|
|
CNameTableEntry *pPlayer;
|
|
|
|
pPlayer = NULL;
|
|
|
|
if ((hResultCode = m_pdnObject->NameTable.FindEntry(pdnMembershipInfo->dpnidPlayer,&pPlayer)) == DPN_OK)
|
|
{
|
|
DWORD dwVersion;
|
|
|
|
dwVersion = pdnMembershipInfo->dwVersion;
|
|
|
|
hResultCode = AddPlayerToGroup(pGroup,pPlayer,&dwVersion);
|
|
pPlayer->Release();
|
|
pPlayer = NULL;
|
|
}
|
|
pGroup->Release();
|
|
pGroup = NULL;
|
|
|
|
DNASSERT(pPlayer == NULL);
|
|
}
|
|
pdnMembershipInfo++;
|
|
|
|
DNASSERT(pGroup == NULL);
|
|
}
|
|
|
|
//
|
|
// Version
|
|
//
|
|
WriteLock();
|
|
SetVersion(pdnNTInfo->dwVersion);
|
|
SetConnectVersion(pdnNTInfo->dwVersion);
|
|
Unlock();
|
|
|
|
hResultCode = DPN_OK;
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::MakeLocalPlayer"
|
|
|
|
void CNameTable::MakeLocalPlayer(CNameTableEntry *const pNTEntry)
|
|
{
|
|
DNASSERT(pNTEntry != NULL);
|
|
DNASSERT(m_pLocalPlayer == NULL);
|
|
|
|
pNTEntry->AddRef();
|
|
pNTEntry->Lock();
|
|
pNTEntry->MakeLocal();
|
|
pNTEntry->Unlock();
|
|
|
|
WriteLock();
|
|
m_pLocalPlayer = pNTEntry;
|
|
Unlock();
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ClearLocalPlayer"
|
|
|
|
void CNameTable::ClearLocalPlayer( void )
|
|
{
|
|
BOOL fInform;
|
|
CNameTableEntry *pNTEntry;
|
|
CConnection *pConnection;
|
|
|
|
fInform = FALSE;
|
|
pNTEntry = NULL;
|
|
pConnection = NULL;
|
|
|
|
WriteLock();
|
|
if (m_pLocalPlayer)
|
|
{
|
|
pNTEntry = m_pLocalPlayer;
|
|
m_pLocalPlayer = NULL;
|
|
|
|
//
|
|
// If the player is available, we will make it unavailable. This will prevent other threads from using it.
|
|
// We will then ensure that we are the only one indicating a DESTROY_PLAYER notification.
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYPLAYERREASON_NORMAL );
|
|
}
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
|
|
if (pNTEntry->IsInUse())
|
|
{
|
|
//
|
|
// Queue destruction notification
|
|
//
|
|
pNTEntry->SetNeedToDestroy();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Notify destruction
|
|
//
|
|
pNTEntry->SetInUse();
|
|
fInform = TRUE;
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
}
|
|
Unlock();
|
|
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->GetConnectionRef(&pConnection);
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// Try to disconnect
|
|
//
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::MakeHostPlayer"
|
|
|
|
void CNameTable::MakeHostPlayer(CNameTableEntry *const pNTEntry)
|
|
{
|
|
BOOL bNotify;
|
|
DPNID dpnid;
|
|
PVOID pvContext;
|
|
|
|
DNASSERT(pNTEntry != NULL);
|
|
DNASSERT(m_pHostPlayer == NULL);
|
|
|
|
pNTEntry->AddRef();
|
|
|
|
pNTEntry->Lock();
|
|
pNTEntry->MakeHost();
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
bNotify = TRUE;
|
|
dpnid = pNTEntry->GetDPNID();
|
|
pvContext = pNTEntry->GetContext();
|
|
}
|
|
else
|
|
{
|
|
bNotify = FALSE;
|
|
}
|
|
pNTEntry->Unlock();
|
|
|
|
WriteLock();
|
|
m_pHostPlayer = pNTEntry;
|
|
Unlock();
|
|
|
|
if (bNotify)
|
|
{
|
|
// Inform user that host has migrated
|
|
DN_UserHostMigrate(m_pdnObject,dpnid,pvContext);
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ClearHostPlayer"
|
|
|
|
void CNameTable::ClearHostPlayer( void )
|
|
{
|
|
BOOL fInform;
|
|
CNameTableEntry *pNTEntry;
|
|
CConnection *pConnection;
|
|
|
|
fInform = FALSE;
|
|
pNTEntry = NULL;
|
|
pConnection = NULL;
|
|
|
|
WriteLock();
|
|
if (m_pHostPlayer)
|
|
{
|
|
pNTEntry = m_pHostPlayer;
|
|
m_pHostPlayer = NULL;
|
|
|
|
//
|
|
// If the player is available, we will make it unavailable. This will prevent other threads from using it.
|
|
// We will then ensure that we are the only one indicating a DESTROY_PLAYER notification.
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYPLAYERREASON_NORMAL );
|
|
}
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
|
|
if (pNTEntry->IsInUse())
|
|
{
|
|
//
|
|
// Queue destruction notification
|
|
//
|
|
pNTEntry->SetNeedToDestroy();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Notify destruction
|
|
//
|
|
pNTEntry->SetInUse();
|
|
fInform = TRUE;
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
}
|
|
Unlock();
|
|
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->GetConnectionRef(&pConnection);
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// Try to disconnect
|
|
//
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Clear the HostPlayer if it has a matching DPNID
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ClearHostWithDPNID"
|
|
|
|
BOOL CNameTable::ClearHostWithDPNID( const DPNID dpnid )
|
|
{
|
|
BOOL fCleared;
|
|
BOOL fInform;
|
|
CNameTableEntry *pNTEntry;
|
|
CConnection *pConnection;
|
|
|
|
fCleared = FALSE;
|
|
fInform = FALSE;
|
|
pNTEntry = NULL;
|
|
pConnection = NULL;
|
|
|
|
WriteLock();
|
|
if (m_pHostPlayer)
|
|
{
|
|
if (m_pHostPlayer->GetDPNID() == dpnid)
|
|
{
|
|
pNTEntry = m_pHostPlayer;
|
|
m_pHostPlayer = NULL;
|
|
|
|
//
|
|
// If the player is available, we will make it unavailable. This will prevent other threads from using it.
|
|
// We will then ensure that we are the only one indicating a DESTROY_PLAYER notification.
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYPLAYERREASON_NORMAL );
|
|
}
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
|
|
if (pNTEntry->IsInUse())
|
|
{
|
|
//
|
|
// Queue destruction notification
|
|
//
|
|
pNTEntry->SetNeedToDestroy();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Notify destruction
|
|
//
|
|
pNTEntry->SetInUse();
|
|
fInform = TRUE;
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
fCleared = TRUE;
|
|
}
|
|
}
|
|
Unlock();
|
|
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->GetConnectionRef(&pConnection);
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// Try to disconnect
|
|
//
|
|
if (pConnection)
|
|
{
|
|
pConnection->Disconnect();
|
|
pConnection->Release();
|
|
pConnection = NULL;
|
|
}
|
|
|
|
return(fCleared);
|
|
}
|
|
|
|
|
|
//
|
|
// Attempt to update the HostPlayer short-cut pointer with a new player entry.
|
|
// This will only be performed if the new entry has a larger version than the
|
|
// existing HostPlayer entry.
|
|
//
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::UpdateHostPlayer"
|
|
|
|
void CNameTable::UpdateHostPlayer( CNameTableEntry *const pNewHost )
|
|
{
|
|
BOOL fInformDelete;
|
|
BOOL fInformMigrate;
|
|
DPNID dpnid;
|
|
PVOID pvContext;
|
|
CNameTableEntry *pNTEntry;
|
|
|
|
DNASSERT( pNewHost != NULL);
|
|
|
|
fInformDelete = FALSE;
|
|
fInformMigrate = FALSE;
|
|
pNTEntry = NULL;
|
|
|
|
WriteLock();
|
|
|
|
//
|
|
// Clear old Host
|
|
//
|
|
if (m_pHostPlayer)
|
|
{
|
|
if (pNewHost->GetVersion() > m_pHostPlayer->GetVersion())
|
|
{
|
|
pNTEntry = m_pHostPlayer;
|
|
m_pHostPlayer = NULL;
|
|
|
|
//
|
|
// If the player is available, we will make it unavailable. This will prevent other threads from using it.
|
|
// We will then ensure that we are the only one indicating a DESTROY_PLAYER notification.
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYPLAYERREASON_NORMAL );
|
|
}
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
|
|
if (pNTEntry->IsInUse())
|
|
{
|
|
//
|
|
// Queue destruction notification
|
|
//
|
|
pNTEntry->SetNeedToDestroy();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Notify destruction
|
|
//
|
|
pNTEntry->SetInUse();
|
|
fInformDelete = TRUE;
|
|
}
|
|
}
|
|
pNTEntry->Unlock();
|
|
}
|
|
}
|
|
|
|
//
|
|
// New Host player
|
|
//
|
|
if (m_pHostPlayer == NULL)
|
|
{
|
|
pNewHost->Lock();
|
|
pNewHost->MakeHost();
|
|
if (pNewHost->IsAvailable())
|
|
{
|
|
fInformMigrate = TRUE;
|
|
dpnid = pNewHost->GetDPNID();
|
|
pvContext = pNewHost->GetContext();
|
|
}
|
|
pNewHost->Unlock();
|
|
pNewHost->AddRef();
|
|
m_pHostPlayer = pNewHost;
|
|
}
|
|
Unlock();
|
|
|
|
//
|
|
// User notifications
|
|
//
|
|
if (pNTEntry)
|
|
{
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
|
|
if (fInformMigrate)
|
|
{
|
|
DN_UserHostMigrate(m_pdnObject,dpnid,pvContext);
|
|
}
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::MakeAllPlayersGroup"
|
|
|
|
void CNameTable::MakeAllPlayersGroup(CNameTableEntry *const pNTEntry)
|
|
{
|
|
DNASSERT(pNTEntry != NULL);
|
|
DNASSERT(m_pAllPlayersGroup == NULL);
|
|
|
|
pNTEntry->AddRef();
|
|
pNTEntry->Lock();
|
|
pNTEntry->MakeAllPlayersGroup();
|
|
pNTEntry->Unlock();
|
|
|
|
WriteLock();
|
|
m_pAllPlayersGroup = pNTEntry;
|
|
Unlock();
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::ClearAllPlayersGroup"
|
|
|
|
void CNameTable::ClearAllPlayersGroup( void )
|
|
{
|
|
BOOL fInform;
|
|
CNameTableEntry *pNTEntry;
|
|
|
|
fInform = FALSE;
|
|
pNTEntry = NULL;
|
|
|
|
WriteLock();
|
|
if (m_pAllPlayersGroup)
|
|
{
|
|
pNTEntry = m_pAllPlayersGroup;
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->IsAvailable())
|
|
{
|
|
pNTEntry->MakeUnavailable();
|
|
fInform = TRUE;
|
|
}
|
|
pNTEntry->Unlock();
|
|
m_pAllPlayersGroup = NULL;
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
Unlock();
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::PopulateConnection"
|
|
|
|
HRESULT CNameTable::PopulateConnection(CConnection *const pConnection)
|
|
{
|
|
HRESULT hResultCode;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
CNameTableEntry *pAllPlayersGroup;
|
|
CGroupMember *pGroupMember;
|
|
CGroupMember *pOldGroupMember;
|
|
BOOL fNotifyCreate;
|
|
BOOL fNotifyAddPlayerToGroup;
|
|
BOOL fNotifyRemovePlayerFromGroup;
|
|
|
|
DNASSERT(pConnection != NULL);
|
|
DNASSERT(pConnection->GetDPNID() != 0);
|
|
|
|
pNTEntry = NULL;
|
|
pAllPlayersGroup = NULL;
|
|
pGroupMember = NULL;
|
|
pOldGroupMember = NULL;
|
|
|
|
if ((hResultCode = FindEntry(pConnection->GetDPNID(),&pNTEntry)) != DPN_OK)
|
|
{
|
|
return(hResultCode);
|
|
}
|
|
DNASSERT(!pNTEntry->IsGroup());
|
|
|
|
//
|
|
// Set the connection for this player
|
|
//
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetConnection() == NULL)
|
|
{
|
|
pNTEntry->SetConnection( pConnection );
|
|
}
|
|
DNASSERT( pNTEntry->IsConnecting() );
|
|
DNASSERT( !pNTEntry->IsAvailable() );
|
|
pNTEntry->StopConnecting();
|
|
pNTEntry->MakeAvailable();
|
|
pNTEntry->Unlock();
|
|
|
|
//
|
|
// Add this player to the ALL_PLAYERS group and make the link active
|
|
//
|
|
if ((hResultCode = m_pdnObject->NameTable.GetAllPlayersGroupRef( &pAllPlayersGroup )) != DPN_OK)
|
|
{
|
|
DPFERR("Could not get ALL_PLAYERS_GROUP reference");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
if ((hResultCode = m_pdnObject->NameTable.AddPlayerToGroup( pAllPlayersGroup,
|
|
pNTEntry,
|
|
NULL)) != DPN_OK)
|
|
{
|
|
DPFERR("Could not add player to AllPlayersGroup");
|
|
DisplayDNError(0,hResultCode);
|
|
goto Failure;
|
|
}
|
|
pAllPlayersGroup->Release();
|
|
pAllPlayersGroup = NULL;
|
|
|
|
fNotifyCreate = FALSE;
|
|
pNTEntry->Lock();
|
|
if (!pNTEntry->IsDisconnecting() && !pNTEntry->IsCreated())
|
|
{
|
|
//
|
|
// We will set the entry as InUse so that any receives will get queued.
|
|
// We will also addref the NotifyRefCount twice. Once for the
|
|
// CREATE_PLAYER notification if there was no INDICATE_CONNECT
|
|
// (so that a corresponding release will generate the DESTROY_PLAYER),
|
|
// and a second one to prevent a premature release from generating
|
|
// the DESTROY_PLAYER before we return from CREATE_PLAYER. We will
|
|
// therefore have to release the refcount as soon as the CREATE_PLAYER
|
|
// returns back to us from the user (setting the context value).
|
|
//
|
|
DNASSERT(!pNTEntry->IsInUse());
|
|
pNTEntry->SetInUse();
|
|
if (!pNTEntry->IsIndicated())
|
|
{
|
|
pNTEntry->NotifyAddRef();
|
|
}
|
|
pNTEntry->NotifyAddRef();
|
|
fNotifyCreate = TRUE;
|
|
}
|
|
pNTEntry->Unlock(); // Release lock during notifications (CREATE_PLAYER, CONNECT_COMPLETE?)
|
|
|
|
if (fNotifyCreate)
|
|
{
|
|
if (!(m_pdnObject->dwFlags & DN_OBJECT_FLAG_CLIENT))
|
|
{
|
|
DNUserCreatePlayer(m_pdnObject,pNTEntry);
|
|
}
|
|
|
|
//
|
|
// Process any queued messages for this player
|
|
//
|
|
pNTEntry->PerformQueuedOperations();
|
|
}
|
|
|
|
//
|
|
// Create any auto-destruct groups belonging to this player
|
|
//
|
|
AutoCreateGroups(pNTEntry);
|
|
|
|
|
|
pNTEntry->Lock();
|
|
|
|
//
|
|
// Ensure this entry is still available (might have been deleted)
|
|
//
|
|
if (!pNTEntry->IsAvailable() || pNTEntry->IsDisconnecting())
|
|
{
|
|
//
|
|
// Reduce outstanding connections (if required)
|
|
//
|
|
if (pNTEntry->GetVersion() <= m_dwConnectVersion)
|
|
{
|
|
DecOutstandingConnections();
|
|
}
|
|
pNTEntry->Unlock();
|
|
goto Failure;
|
|
}
|
|
|
|
pBilink = pNTEntry->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pNTEntry->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkGroups);
|
|
pGroupMember->AddRef();
|
|
pNTEntry->Unlock();
|
|
|
|
DNASSERT(pGroupMember->GetGroup() != NULL);
|
|
DNASSERT(pGroupMember->GetPlayer() != NULL);
|
|
|
|
fNotifyAddPlayerToGroup = FALSE;
|
|
fNotifyRemovePlayerFromGroup = FALSE;
|
|
|
|
pGroupMember->GetGroup()->Lock();
|
|
pGroupMember->Lock();
|
|
DNASSERT(pGroupMember->GetGroupConnection() != NULL);
|
|
if (!pGroupMember->IsAvailable() && !pGroupMember->IsNeedToAdd() && !pGroupMember->GetGroupConnection()->IsConnected())
|
|
{
|
|
//
|
|
// We will only indicate this up if the group has been created
|
|
// We don't need to see if the player has been created since he should have been and the NotifyRefCount
|
|
// on the player's entry for this group member will still be there
|
|
//
|
|
if ( pGroupMember->GetGroup()->IsCreated()
|
|
&& !pGroupMember->GetGroup()->IsDisconnecting()
|
|
&& !pGroupMember->GetGroup()->IsAllPlayersGroup())
|
|
{
|
|
pGroupMember->SetNeedToAdd();
|
|
fNotifyAddPlayerToGroup = TRUE;
|
|
}
|
|
}
|
|
pGroupMember->GetGroup()->Unlock();
|
|
pGroupMember->Unlock();
|
|
|
|
if (fNotifyAddPlayerToGroup)
|
|
{
|
|
DNASSERT(pGroupMember->GetGroupConnection()->GetConnection() == NULL);
|
|
pGroupMember->Lock();
|
|
pGroupMember->GetGroupConnection()->Lock();
|
|
pGroupMember->GetGroupConnection()->SetConnection(pConnection);
|
|
pGroupMember->GetGroupConnection()->Unlock();
|
|
pGroupMember->MakeAvailable();
|
|
pGroupMember->Unlock();
|
|
|
|
DNUserAddPlayerToGroup( m_pdnObject,pGroupMember->GetGroup(),pGroupMember->GetPlayer());
|
|
|
|
pGroupMember->Lock();
|
|
pGroupMember->ClearNeedToAdd();
|
|
if (pGroupMember->IsNeedToRemove())
|
|
{
|
|
fNotifyRemovePlayerFromGroup = TRUE;
|
|
}
|
|
pGroupMember->Unlock();
|
|
}
|
|
|
|
if (fNotifyRemovePlayerFromGroup)
|
|
{
|
|
DNUserRemovePlayerFromGroup(m_pdnObject,pGroupMember->GetGroup(),pGroupMember->GetPlayer());
|
|
}
|
|
|
|
//
|
|
// Release old group member and transfer reference
|
|
//
|
|
if (pOldGroupMember)
|
|
{
|
|
pOldGroupMember->Release();
|
|
pOldGroupMember = NULL;
|
|
}
|
|
pOldGroupMember = pGroupMember;
|
|
pGroupMember = NULL;
|
|
|
|
pNTEntry->Lock();
|
|
//
|
|
// Avoid infinite loops by ensuring that we are not on a "disconnected" entry
|
|
//
|
|
if ((pBilink->GetNext() != &pNTEntry->m_bilinkMembership) && (pBilink->GetNext() == pBilink))
|
|
{
|
|
//
|
|
// We have an invalid entry - need to restart
|
|
//
|
|
pBilink = pNTEntry->m_bilinkMembership.GetNext();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We either have a valid entry or we're finished
|
|
//
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
|
|
pNTEntry->Unlock();
|
|
|
|
if (pOldGroupMember)
|
|
{
|
|
pOldGroupMember->Release();
|
|
pOldGroupMember = NULL;
|
|
}
|
|
|
|
//
|
|
// Reduce outstanding connections
|
|
//
|
|
if (pNTEntry->GetVersion() <= m_dwConnectVersion)
|
|
{
|
|
DecOutstandingConnections();
|
|
}
|
|
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
|
|
Exit:
|
|
DNASSERT(pNTEntry == NULL);
|
|
DNASSERT(pGroupMember == NULL);
|
|
DNASSERT(pOldGroupMember == NULL);
|
|
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pNTEntry)
|
|
{
|
|
pNTEntry->Release();
|
|
pNTEntry = NULL;
|
|
}
|
|
if (pAllPlayersGroup)
|
|
{
|
|
pAllPlayersGroup->Release();
|
|
pAllPlayersGroup = NULL;
|
|
}
|
|
if (pGroupMember)
|
|
{
|
|
pGroupMember->Release();
|
|
pGroupMember = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// This will generate ADD_PLAYER_TO_GROUP messages for all of the CREATE'd players in a group
|
|
// (for whom a notification has not been posted)
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::PopulateGroup"
|
|
|
|
HRESULT CNameTable::PopulateGroup(CNameTableEntry *const pGroup)
|
|
{
|
|
HRESULT hResultCode;
|
|
BOOL fNotifyAddPlayerToGroup;
|
|
BOOL fNotifyRemovePlayerFromGroup;
|
|
CBilink *pBilink;
|
|
CGroupMember *pGroupMember;
|
|
CGroupMember *pOldGroupMember;
|
|
CNameTableEntry *pPlayer;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pGroup [0x%p]",pGroup);
|
|
|
|
DNASSERT(pGroup != NULL);
|
|
|
|
hResultCode = DPN_OK;
|
|
pPlayer = NULL;
|
|
pGroupMember = NULL;
|
|
pOldGroupMember = NULL;
|
|
|
|
if (!pGroup->IsGroup())
|
|
{
|
|
hResultCode = DPNERR_INVALIDGROUP;
|
|
goto Failure;
|
|
}
|
|
|
|
pGroup->Lock();
|
|
pBilink = pGroup->m_bilinkMembership.GetNext();
|
|
while (pBilink != &pGroup->m_bilinkMembership)
|
|
{
|
|
pGroupMember = CONTAINING_OBJECT(pBilink,CGroupMember,m_bilinkPlayers);
|
|
pGroupMember->AddRef();
|
|
pGroupMember->Lock();
|
|
DNASSERT(pGroupMember->GetGroup() != NULL);
|
|
DNASSERT(pGroupMember->GetPlayer() != NULL);
|
|
DNASSERT(pGroupMember->GetGroup() == pGroup);
|
|
pGroupMember->GetPlayer()->AddRef();
|
|
pPlayer = pGroupMember->GetPlayer();
|
|
pGroup->Unlock();
|
|
pGroupMember->Unlock();
|
|
|
|
fNotifyAddPlayerToGroup = FALSE;
|
|
fNotifyRemovePlayerFromGroup = FALSE;
|
|
|
|
pGroup->Lock();
|
|
pPlayer->Lock();
|
|
pGroupMember->Lock();
|
|
DNASSERT( pGroupMember->GetGroupConnection() != NULL );
|
|
if ( pPlayer->IsCreated()
|
|
&& !pPlayer->IsDisconnecting()
|
|
&& pGroup->IsCreated()
|
|
&& !pGroup->IsDisconnecting()
|
|
&& !pGroupMember->IsAvailable()
|
|
&& !pGroupMember->IsNeedToAdd()
|
|
&& !pGroupMember->GetGroupConnection()->IsConnected() )
|
|
{
|
|
pGroupMember->MakeAvailable();
|
|
pGroupMember->SetNeedToAdd();
|
|
fNotifyAddPlayerToGroup = TRUE;
|
|
}
|
|
pGroup->Unlock();
|
|
pPlayer->Unlock();
|
|
pGroupMember->Unlock();
|
|
|
|
if (fNotifyAddPlayerToGroup)
|
|
{
|
|
DNUserAddPlayerToGroup(m_pdnObject,pGroup,pPlayer);
|
|
|
|
pGroupMember->Lock();
|
|
pGroupMember->ClearNeedToAdd();
|
|
if (pGroupMember->IsNeedToRemove())
|
|
{
|
|
fNotifyRemovePlayerFromGroup = TRUE;
|
|
}
|
|
pGroupMember->Unlock();
|
|
}
|
|
if (fNotifyRemovePlayerFromGroup)
|
|
{
|
|
RemovePlayerFromGroup(pGroup,pPlayer,NULL);
|
|
}
|
|
|
|
pPlayer->Release();
|
|
pPlayer = NULL;
|
|
|
|
//
|
|
// Release old group member and transfer reference
|
|
//
|
|
if (pOldGroupMember)
|
|
{
|
|
pOldGroupMember->Release();
|
|
pOldGroupMember = NULL;
|
|
}
|
|
pOldGroupMember = pGroupMember;
|
|
pGroupMember = NULL;
|
|
|
|
pGroup->Lock();
|
|
if (pBilink->IsEmpty())
|
|
{
|
|
pBilink = pGroup->m_bilinkMembership.GetNext();
|
|
}
|
|
else
|
|
{
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
pGroup->Unlock();
|
|
|
|
if (pOldGroupMember)
|
|
{
|
|
pOldGroupMember->Release();
|
|
pOldGroupMember = NULL;
|
|
}
|
|
|
|
Exit:
|
|
DNASSERT(pPlayer == NULL);
|
|
DNASSERT(pGroupMember == NULL);
|
|
DNASSERT(pOldGroupMember == NULL);
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pPlayer)
|
|
{
|
|
pPlayer->Release();
|
|
pPlayer = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// This will generate CREATE_GROUP messages for all of the auto-destruct groups owned by a particular player
|
|
//
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::AutoCreateGroups"
|
|
|
|
HRESULT CNameTable::AutoCreateGroups(CNameTableEntry *const pPlayer)
|
|
{
|
|
HRESULT hResultCode;
|
|
BOOL fNotify;
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pGroup;
|
|
CNameTableEntry *pOldGroup;
|
|
|
|
DPFX(DPFPREP, 6,"Parameters: pPlayer [0x%p]",pPlayer);
|
|
|
|
DNASSERT(pPlayer != NULL);
|
|
|
|
pGroup = NULL;
|
|
pOldGroup = NULL;
|
|
|
|
if (pPlayer->IsGroup())
|
|
{
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
goto Failure;
|
|
}
|
|
|
|
ReadLock();
|
|
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
while (pBilink != &m_bilinkGroups)
|
|
{
|
|
pGroup = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pGroup->AddRef();
|
|
Unlock();
|
|
|
|
fNotify = FALSE;
|
|
pGroup->Lock();
|
|
if ( pGroup->IsAutoDestructGroup()
|
|
&& (pGroup->GetOwner() == pPlayer->GetDPNID())
|
|
&& !pGroup->IsAvailable()
|
|
&& !pGroup->IsDisconnecting() )
|
|
{
|
|
pGroup->MakeAvailable();
|
|
pGroup->NotifyAddRef();
|
|
pGroup->NotifyAddRef();
|
|
pGroup->SetInUse();
|
|
fNotify = TRUE;
|
|
}
|
|
pGroup->Unlock();
|
|
|
|
if (fNotify)
|
|
{
|
|
DNASSERT(!pGroup->IsAllPlayersGroup());
|
|
DNUserCreateGroup(m_pdnObject,pGroup);
|
|
|
|
pGroup->PerformQueuedOperations();
|
|
|
|
//
|
|
// Attempt to populate group with connected players
|
|
//
|
|
PopulateGroup(pGroup);
|
|
}
|
|
|
|
//
|
|
// Release old group and transfer reference
|
|
//
|
|
if (pOldGroup)
|
|
{
|
|
pOldGroup->Release();
|
|
pOldGroup = NULL;
|
|
}
|
|
pOldGroup = pGroup;
|
|
pGroup = NULL;
|
|
|
|
ReadLock();
|
|
if (pBilink->IsEmpty())
|
|
{
|
|
//
|
|
// We were removed from the list of groups, so re-start at the beginning
|
|
//
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
}
|
|
else
|
|
{
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if (pOldGroup)
|
|
{
|
|
pOldGroup->Release();
|
|
pOldGroup = NULL;
|
|
}
|
|
|
|
hResultCode = DPN_OK;
|
|
|
|
Exit:
|
|
DNASSERT(pGroup == NULL);
|
|
DNASSERT(pOldGroup == NULL);
|
|
|
|
DPFX(DPFPREP, 6,"Returning: [0x%lx]",hResultCode);
|
|
return(hResultCode);
|
|
|
|
Failure:
|
|
if (pGroup)
|
|
{
|
|
pGroup->Release();
|
|
pGroup = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::AutoDestructGroups"
|
|
|
|
HRESULT CNameTable::AutoDestructGroups(const DPNID dpnid)
|
|
{
|
|
CBilink *pBilink;
|
|
CNameTableEntry *pNTEntry;
|
|
CNameTableEntry *pOldNTEntry;
|
|
|
|
pNTEntry = NULL;
|
|
pOldNTEntry = NULL;
|
|
|
|
ReadLock();
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
while (pBilink != &m_bilinkGroups)
|
|
{
|
|
pNTEntry = CONTAINING_OBJECT(pBilink,CNameTableEntry,m_bilinkEntries);
|
|
pNTEntry->AddRef();
|
|
|
|
Unlock();
|
|
|
|
if (pNTEntry->IsAutoDestructGroup() && (pNTEntry->GetOwner() == dpnid))
|
|
{
|
|
pNTEntry->Lock();
|
|
if (pNTEntry->GetDestroyReason() == 0)
|
|
{
|
|
pNTEntry->SetDestroyReason( DPNDESTROYGROUPREASON_AUTODESTRUCTED );
|
|
}
|
|
pNTEntry->Unlock();
|
|
DeleteGroup(pNTEntry->GetDPNID(),NULL);
|
|
}
|
|
|
|
//
|
|
// Release old entry and transfer reference
|
|
//
|
|
if (pOldNTEntry)
|
|
{
|
|
pOldNTEntry->Release();
|
|
pOldNTEntry = NULL;
|
|
}
|
|
pOldNTEntry = pNTEntry;
|
|
pNTEntry = NULL;
|
|
|
|
ReadLock();
|
|
|
|
//
|
|
// Avoid infinite loops by ensuring that we are not on a "disconnected" entry
|
|
//
|
|
if ((pBilink->GetNext() != &m_bilinkGroups) && (pBilink->GetNext() == pBilink))
|
|
{
|
|
//
|
|
// We have an invalid entry - need to restart
|
|
//
|
|
pBilink = m_bilinkGroups.GetNext();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We either have a valid entry or we're finished
|
|
//
|
|
pBilink = pBilink->GetNext();
|
|
}
|
|
}
|
|
Unlock();
|
|
|
|
if (pOldNTEntry)
|
|
{
|
|
pOldNTEntry->Release();
|
|
pOldNTEntry = NULL;
|
|
}
|
|
|
|
DNASSERT(pNTEntry == NULL);
|
|
DNASSERT(pOldNTEntry == NULL);
|
|
|
|
return(DPN_OK);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::DecOutstandingConnections"
|
|
|
|
void CNameTable::DecOutstandingConnections( void )
|
|
{
|
|
LONG lRefCount;
|
|
|
|
lRefCount = DNInterlockedDecrement(&m_lOutstandingConnections);
|
|
DNASSERT(lRefCount >= 0);
|
|
if (lRefCount == 0)
|
|
{
|
|
CAsyncOp *pConnectParent;
|
|
|
|
pConnectParent = NULL;
|
|
|
|
//
|
|
// Clear connect handle from DirectNetObject if we are connected
|
|
//
|
|
DNEnterCriticalSection(&m_pdnObject->csDirectNetObject);
|
|
if (m_pdnObject->dwFlags & (DN_OBJECT_FLAG_CONNECTED|DN_OBJECT_FLAG_CONNECTING))
|
|
{
|
|
DPFX(DPFPREP, 5,"Clearing connection operation from DirectNetObject");
|
|
pConnectParent = m_pdnObject->pConnectParent;
|
|
m_pdnObject->pConnectParent = NULL;
|
|
}
|
|
DNLeaveCriticalSection(&m_pdnObject->csDirectNetObject);
|
|
|
|
if (pConnectParent)
|
|
{
|
|
//
|
|
// We will set the connect parent as complete and remove this from the parent's (if it exists - it will be the connect handle)
|
|
// bilink of children to prevent a race condition of the connect being cancelled from above
|
|
//
|
|
pConnectParent->Lock();
|
|
pConnectParent->SetComplete();
|
|
pConnectParent->Unlock();
|
|
pConnectParent->Orphan();
|
|
|
|
pConnectParent->Release();
|
|
pConnectParent = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::GetLocalPlayerRef"
|
|
|
|
HRESULT CNameTable::GetLocalPlayerRef( CNameTableEntry **const ppNTEntry )
|
|
{
|
|
HRESULT hResultCode;
|
|
|
|
ReadLock();
|
|
if (m_pLocalPlayer)
|
|
{
|
|
m_pLocalPlayer->AddRef();
|
|
*ppNTEntry = m_pLocalPlayer;
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
}
|
|
Unlock();
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::GetHostPlayerRef"
|
|
|
|
HRESULT CNameTable::GetHostPlayerRef( CNameTableEntry **const ppNTEntry )
|
|
{
|
|
HRESULT hResultCode;
|
|
|
|
ReadLock();
|
|
if (m_pHostPlayer)
|
|
{
|
|
m_pHostPlayer->AddRef();
|
|
*ppNTEntry = m_pHostPlayer;
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
}
|
|
Unlock();
|
|
return(hResultCode);
|
|
}
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CNameTable::GetAllPlayersGroupRef"
|
|
|
|
HRESULT CNameTable::GetAllPlayersGroupRef( CNameTableEntry **const ppNTEntry )
|
|
{
|
|
HRESULT hResultCode;
|
|
|
|
ReadLock();
|
|
if (m_pAllPlayersGroup)
|
|
{
|
|
m_pAllPlayersGroup->AddRef();
|
|
*ppNTEntry = m_pAllPlayersGroup;
|
|
hResultCode = DPN_OK;
|
|
}
|
|
else
|
|
{
|
|
hResultCode = DPNERR_INVALIDPLAYER;
|
|
}
|
|
Unlock();
|
|
return(hResultCode);
|
|
}
|