/*========================================================================== * * 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(reinterpret_cast(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(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(static_cast(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(0); pNewArray[m_dwLastFreeEntry].dwFlags = 0; // Link new FREE entries for (dw = m_dwNameTableSize ; dw < m_dwLastFreeEntry ; dw++) { pNewArray[dw].pNameTableEntry = reinterpret_cast(static_cast(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(reinterpret_cast(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(reinterpret_cast(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(0); pNewArray[m_dwLastFreeEntry].dwFlags = 0; for (dw = 0 ; dw < m_dwLastFreeEntry ; dw++) { pNewArray[dw].pNameTableEntry = reinterpret_cast(static_cast(dw+1)); pNewArray[dw].dwFlags = 0; } m_dwFirstFreeEntry = static_cast(reinterpret_cast(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(reinterpret_cast(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(static_cast(dwIndex))) { m_NameTableArray[dw].pNameTableEntry = m_NameTableArray[dwIndex].pNameTableEntry; if (m_dwLastFreeEntry == dwIndex) { m_dwLastFreeEntry = dw; } bFound = TRUE; } else { dw = static_cast(reinterpret_cast(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(static_cast(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(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.dwEntryCount entries) // (DN_NAMETABLE_INFO.dwMembershipCount entries) // ... // // // // NameTable Info // dwVersion = pTarget->GetVersion(); bOutOfSpace = FALSE; pdnNTInfo = static_cast(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(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(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); }