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