You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
849 lines
28 KiB
849 lines
28 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
|
|
#include "cbase.h" |
|
#include "gcsdk/gcsdk.h" |
|
#include "base_gcmessages.h" |
|
#include "playergroupmanager.h" |
|
#include "gcsdk/accountdetails.h" |
|
|
|
using namespace GCSDK; |
|
|
|
CPlayerGroupManager::mapPlayersMemcacheJobCount_t CPlayerGroupManager::sm_mapPlayersMemcacheJobCount; |
|
|
|
CPlayerGroupManager::CPlayerGroupManager() : |
|
m_hashPlayerGroupIDLocks( k_nLocksRunInterval / k_cMicroSecPerShellFrame ) |
|
{ |
|
m_hashPlayerGroupIDLocks.Init( k_cGCLocksInit, k_cBucketGCLocks ); |
|
m_GroupIDGenerationLock.SetName( "GroupIDGenerationLock" ); |
|
m_GroupIDGenerationLock.SetLockType( kGroupIDGenerationLockType ); |
|
} |
|
|
|
PlayerGroupID_t CPlayerGroupManager::GeneratePlayerGroupID() |
|
{ |
|
return GGCHost()->GenerateGID(); |
|
} |
|
|
|
void CPlayerGroupManager::MemcachedUpdateAllMemberAssocation( IPlayerGroup *pPlayerGroup ) |
|
{ |
|
CUtlVector<CUtlString> memcachedMemberKeys; |
|
CUtlVector<CGCBase::GCMemcachedBuffer_t> memcachedMemberValues; |
|
|
|
CUtlBuffer memcachedMemberValue; |
|
memcachedMemberValue.PutInt64( (int64) pPlayerGroup->GetGroupID() ); |
|
|
|
for ( int i = 0; i < pPlayerGroup->GetNumMembers(); ++i ) |
|
{ |
|
CUtlString &memberKey = memcachedMemberKeys[ memcachedMemberKeys.AddToTail() ]; |
|
memberKey.Format( "GC:%u:%sMEMBER:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), pPlayerGroup->GetMember(i).ConvertToUint64() ); |
|
|
|
CGCBase::GCMemcachedBuffer_t &memcachedBuffer = memcachedMemberValues[ memcachedMemberValues.AddToTail() ]; |
|
memcachedBuffer.m_pubData = memcachedMemberValue.Base(); |
|
memcachedBuffer.m_cubData = memcachedMemberValue.TellPut(); |
|
} |
|
|
|
GGCBase()->BMemcachedSet( memcachedMemberKeys, memcachedMemberValues ); |
|
} |
|
|
|
void CPlayerGroupManager::MemcachedRemoveAllMemberAssocation( IPlayerGroup *pPlayerGroup ) |
|
{ |
|
CUtlVector<CUtlString> memcachedMemberKeys; |
|
|
|
for ( int i = 0; i < pPlayerGroup->GetNumMembers(); ++i ) |
|
{ |
|
CUtlString &memberKey = memcachedMemberKeys[ memcachedMemberKeys.AddToTail() ]; |
|
memberKey.Format( "GC:%u:%sMEMBER:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), pPlayerGroup->GetMember(i).ConvertToUint64() ); |
|
} |
|
|
|
GGCBase()->BMemcachedDelete( memcachedMemberKeys ); |
|
} |
|
|
|
void CPlayerGroupManager::MemcachedUpdateMemberAssocation( PlayerGroupID_t nPlayerGroupID, const CSteamID &steamID ) |
|
{ |
|
CUtlBuffer memcachedMemberValue; |
|
memcachedMemberValue.PutInt64( (int64) nPlayerGroupID ); |
|
CUtlString memberKey; |
|
memberKey.Format( "GC:%u:%sMEMBER:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), steamID.ConvertToUint64() ); |
|
|
|
GGCBase()->BMemcachedSet( memberKey, memcachedMemberValue ); |
|
} |
|
|
|
void CPlayerGroupManager::MemcachedRemoveMemberAssocation( PlayerGroupID_t nPlayerGroupID, const CSteamID &steamID ) |
|
{ |
|
CUtlString memberKey; |
|
memberKey.Format( "GC:%u:%sMEMBER:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), steamID.ConvertToUint64() ); |
|
GGCBase()->BMemcachedDelete( memberKey ); |
|
} |
|
|
|
bool CPlayerGroupManager::BYldAddMemberToGroup( PlayerGroupID_t nPlayerGroupID, const CSteamID &steamIDNewMember ) |
|
{ |
|
// If member is already in a group, abort |
|
IPlayerGroup *pOldPlayerGroup = FindGroupByMemberID( steamIDNewMember ); |
|
if ( pOldPlayerGroup ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "BYldAddMemberToGroup not adding member: %s to player group: %016llx as he's already in a group\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
return false; |
|
} |
|
|
|
// check group exists |
|
IPlayerGroup *pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
Assert( pPlayerGroup ); |
|
if ( !pPlayerGroup ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "BYldAddMemberToGroup not adding member: %s to player group: %016llx as that group does not exist\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
return false; |
|
} |
|
|
|
// check member isn't already in the group |
|
if ( pPlayerGroup->GetMemberIndexBySteamID( steamIDNewMember ) != -1 ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "BYldAddMemberToGroup not adding member: %s to player group: %016llx as he's already in the group\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
UnlockGroupID( nPlayerGroupID ); |
|
return false; |
|
} |
|
|
|
// Lock the user's cache so we can add the group to it |
|
CGCSharedObjectCache *pCache = GGCBase()->YieldingGetLockedSOCache( steamIDNewMember ); |
|
if ( !pCache ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "BYldAddMemberToGroup not adding member: %s to player group: %016llx as his cache could not be loaded\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
GGCBase()->UnlockSteamID( steamIDNewMember ); |
|
UnlockGroupID( nPlayerGroupID ); |
|
return false; |
|
} |
|
|
|
// If member is already in a group, abort (must do this again after yielding as another job could have put him in a group while we yielded) |
|
pOldPlayerGroup = FindGroupByMemberID( steamIDNewMember ); |
|
if ( pOldPlayerGroup ) |
|
{ |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "BYldAddMemberToGroup not adding member: %s to player group: %016llx as he's already in a group (2)\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
GGCBase()->UnlockSteamID( steamIDNewMember ); |
|
UnlockGroupID( nPlayerGroupID ); |
|
return false; |
|
} |
|
|
|
// Add shared object to this member's cache, before dirtying fields |
|
pCache->AddObject( pPlayerGroup->GetSharedObject() ); |
|
|
|
// Add member to group. This will dirty shared object fields. |
|
pPlayerGroup->AddMember( steamIDNewMember ); |
|
m_mapMemberToGroup.InsertOrReplace( steamIDNewMember, pPlayerGroup ); |
|
GGCBase()->UnlockSteamID( steamIDNewMember ); |
|
|
|
// Update memcached |
|
MemcachedUpdateMemberAssocation( nPlayerGroupID, steamIDNewMember ); |
|
|
|
// notify derived classes |
|
YldOnPlayerJoinedGroup( pPlayerGroup, steamIDNewMember ); |
|
|
|
UnlockGroupID( nPlayerGroupID ); |
|
|
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "Steam id: %s added to player group: %016llx\n", steamIDNewMember.Render(), nPlayerGroupID ); |
|
|
|
return true; |
|
} |
|
|
|
void CPlayerGroupManager::YldRemoveMemberFromGroup( PlayerGroupID_t nPlayerGroupID, const CSteamID &steamIDRemovingMember ) |
|
{ |
|
// check group exists |
|
IPlayerGroup *pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
Assert( pPlayerGroup ); |
|
if ( !pPlayerGroup ) |
|
return; |
|
|
|
// check member is in the group |
|
if ( pPlayerGroup->GetMemberIndexBySteamID( steamIDRemovingMember ) == -1 ) |
|
{ |
|
UnlockGroupID( nPlayerGroupID ); |
|
return; |
|
} |
|
|
|
pPlayerGroup->RemoveMember( steamIDRemovingMember ); |
|
m_mapMemberToGroup.Remove( steamIDRemovingMember ); |
|
|
|
// Update memcached |
|
MemcachedRemoveMemberAssocation( nPlayerGroupID, steamIDRemovingMember ); |
|
|
|
CGCSharedObjectCache *pCache = GGCBase()->YieldingGetLockedSOCache( steamIDRemovingMember ); |
|
if ( pCache ) |
|
{ |
|
pCache->RemoveObject( *pPlayerGroup->GetSharedObject() ); |
|
pCache->SendAllNetworkUpdates(); |
|
} |
|
GGCBase()->UnlockSteamID( steamIDRemovingMember ); |
|
|
|
YldOnPlayerLeftGroup( pPlayerGroup, steamIDRemovingMember ); |
|
|
|
UnlockGroupID( nPlayerGroupID ); |
|
|
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "Steam id: %s removed from player group: %016llx\n", steamIDRemovingMember.Render(), nPlayerGroupID ); |
|
} |
|
|
|
IPlayerGroup* CPlayerGroupManager::FindGroup( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
hashPlayerGroups_t::IndexType_t nPlayerGroupIndex = m_mapGroups.Find( nPlayerGroupID ); |
|
if ( nPlayerGroupIndex == m_mapGroups.InvalidIndex() ) |
|
return NULL; |
|
|
|
return m_mapGroups.Element( nPlayerGroupIndex ); |
|
} |
|
|
|
IPlayerGroup* CPlayerGroupManager::YldFindAndLockGroup( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
hashPlayerGroups_t::IndexType_t nPlayerGroupIndex = m_mapGroups.Find( nPlayerGroupID ); |
|
if ( nPlayerGroupIndex == m_mapGroups.InvalidIndex() ) |
|
return NULL; |
|
|
|
// try to lock it |
|
if ( !BYieldingLockGroupID( nPlayerGroupID ) ) |
|
return NULL; |
|
|
|
// check the group is still valid |
|
nPlayerGroupIndex = m_mapGroups.Find( nPlayerGroupID ); |
|
if ( nPlayerGroupIndex == m_mapGroups.InvalidIndex() ) |
|
{ |
|
// group went away, unlock and abort |
|
UnlockGroupID( nPlayerGroupID ); |
|
return NULL; |
|
} |
|
|
|
return m_mapGroups.Element( nPlayerGroupIndex ); |
|
} |
|
|
|
IPlayerGroup* CPlayerGroupManager::FindGroupByMemberID( const CSteamID &steamID ) |
|
{ |
|
mapMembersToPlayerGroups_t::IndexType_t nPlayerGroupIndex = m_mapMemberToGroup.Find( steamID ); |
|
if ( nPlayerGroupIndex == m_mapMemberToGroup.InvalidIndex() ) |
|
return NULL; |
|
|
|
return m_mapMemberToGroup.Element( nPlayerGroupIndex ); |
|
} |
|
|
|
IPlayerGroup* CPlayerGroupManager::YldFindAndLockGroupByMemberID( const CSteamID &steamID ) |
|
{ |
|
mapMembersToPlayerGroups_t::IndexType_t nPlayerGroupIndex = m_mapMemberToGroup.Find( steamID ); |
|
if ( nPlayerGroupIndex == m_mapMemberToGroup.InvalidIndex() ) |
|
return NULL; |
|
|
|
IPlayerGroup *pPlayerGroup = m_mapMemberToGroup.Element( nPlayerGroupIndex ); |
|
|
|
return YldFindAndLockGroup( pPlayerGroup->GetGroupID() ); |
|
} |
|
|
|
bool CPlayerGroupManager::BYieldingLockGroupID( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
AssertRunningJob(); |
|
|
|
// lookup |
|
CLock *pLock = m_hashPlayerGroupIDLocks.PvRecordFind( nPlayerGroupID ); |
|
if ( pLock == NULL ) |
|
{ |
|
// no lock yet, insert one |
|
pLock = m_hashPlayerGroupIDLocks.PvRecordInsert( nPlayerGroupID ); |
|
Assert( pLock != NULL ); |
|
if ( pLock == NULL ) |
|
{ |
|
EmitInfo( SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "Unable to create lock for PlayerGroup:%u\n", nPlayerGroupID ); |
|
return false; |
|
} |
|
pLock->SetName( "PlayerGroup:", nPlayerGroupID ); |
|
pLock->SetLockType( GetGroupLockType() ); |
|
} |
|
|
|
return GJobCur().BYieldingAcquireLock( pLock ); |
|
} |
|
|
|
void CPlayerGroupManager::UnlockGroupID( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
AssertRunningJob(); |
|
|
|
// lookup |
|
CLock *pLock = m_hashPlayerGroupIDLocks.PvRecordFind( nPlayerGroupID ); |
|
Assert( pLock ); |
|
if ( pLock == NULL ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( pLock->GetJobLocking() != &GJobCur() ) |
|
{ |
|
AssertMsg1( false, "UnlockGroupID() called when job (%s) doesn't own the lock", GJobCur().GetName() ); |
|
return; |
|
} |
|
|
|
GJobCur().ReleaseLock( pLock ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns true if the specified PlayerGroupID_t is locked |
|
//----------------------------------------------------------------------------- |
|
bool CPlayerGroupManager::IsGroupIDLocked( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
CLock *pLock = m_hashPlayerGroupIDLocks.PvRecordFind( nPlayerGroupID ); |
|
if ( pLock ) |
|
return pLock->BIsLocked(); |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Marks the start of a frame |
|
//----------------------------------------------------------------------------- |
|
void CPlayerGroupManager::StartFrameSchedule() |
|
{ |
|
m_hashPlayerGroupIDLocks.StartFrameSchedule( true ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets rid of any locks that haven't been touched in a while |
|
//----------------------------------------------------------------------------- |
|
bool CPlayerGroupManager::BExpireLocks( CLimitTimer &limitTimer ) |
|
{ |
|
VPROF_BUDGET( "Expire PlayerGroup Locks", VPROF_BUDGETGROUP_STEAM ); |
|
|
|
for ( CLock *pLock = m_hashPlayerGroupIDLocks.PvRecordRun(); NULL != pLock; pLock = m_hashPlayerGroupIDLocks.PvRecordRun() ) |
|
{ |
|
if ( !pLock->BIsLocked() && pLock->GetMicroSecondsSinceLock() > k_cMicroSecLockLifetime ) |
|
{ |
|
m_hashPlayerGroupIDLocks.Remove( pLock ); |
|
} |
|
|
|
if ( limitTimer.BLimitReached() ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
void CPlayerGroupManager::YldDestroyGroupIfEmpty( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
IPlayerGroup *pGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( pGroup ) |
|
{ |
|
if ( pGroup->GetNumMembers() <= 0 ) |
|
{ |
|
YldDestroyGroup( nPlayerGroupID ); |
|
} |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YldDestroyGroup( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
IPlayerGroup* pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( !pPlayerGroup ) |
|
return; |
|
|
|
// allow derived classes to clean up |
|
YldOnGroupDestroyed( pPlayerGroup ); |
|
|
|
// remove all members |
|
while( pPlayerGroup->GetNumMembers() > 0 ) |
|
{ |
|
YldRemoveMemberFromGroup( nPlayerGroupID, pPlayerGroup->GetMember( 0 ) ); |
|
} |
|
|
|
m_mapGroups.Remove( nPlayerGroupID ); |
|
|
|
// Remove the group from memcached |
|
GGCBase()->BMemcachedDelete( CFmtStr( "GC:%u:%s:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), nPlayerGroupID ).Access() ); |
|
|
|
delete pPlayerGroup; |
|
} |
|
|
|
// Update memcached and players when the group changes |
|
void CPlayerGroupManager::SendGroupStorageAndNetworkUpdate( IPlayerGroup *pPlayerGroup ) |
|
{ |
|
for ( int i = 0; i < pPlayerGroup->GetNumMembers(); i++ ) |
|
{ |
|
CGCUserSession *pMemberSession = GGCBase()->FindUserSession( pPlayerGroup->GetMember( i ) ); |
|
if ( !pMemberSession ) |
|
continue; |
|
|
|
pMemberSession->GetSOCache()->SendAllNetworkUpdates(); |
|
} |
|
|
|
// Update memcached |
|
VPROF_BUDGET( "SendGroupStorageAndNetworkUpdate - Memcached", VPROF_BUDGETGROUP_STEAM ); |
|
|
|
CUtlString memcachedGroupKey; |
|
memcachedGroupKey.Format( "GC:%u:%s:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), pPlayerGroup->GetGroupID() ); |
|
|
|
CUtlBuffer memcachedGroupValue; |
|
CSharedObject *pObj = pPlayerGroup->GetSharedObject(); |
|
Assert( pObj ); |
|
if ( !pObj ) |
|
{ |
|
return; |
|
} |
|
pObj->BAddFullCreateToMessage( memcachedGroupValue ); |
|
|
|
GGCBase()->BMemcachedSet( memcachedGroupKey, memcachedGroupValue ); |
|
} |
|
|
|
void CPlayerGroupManager::DumpGroups() |
|
{ |
|
// Sort groups so we can dump in-order |
|
CUtlSortVector<PlayerGroupID_t> vecIDs( 0, m_mapGroups.Count() ); |
|
FOR_EACH_MAP_FAST( m_mapGroups, i ) |
|
{ |
|
vecIDs.InsertNoSort( m_mapGroups.Key( i ) ); |
|
} |
|
vecIDs.RedoSort( true ); |
|
|
|
FOR_EACH_VEC( vecIDs, i ) |
|
{ |
|
IPlayerGroup *pPlayerGroup = m_mapGroups[m_mapGroups.Find( vecIDs[i] )]; |
|
if ( !pPlayerGroup || !pPlayerGroup->GetSharedObject() ) |
|
continue; |
|
|
|
EmitInfo( SPEW_SHAREDOBJ, SPEW_ALWAYS, LOG_ALWAYS, "Group: %llu NumMembers: %d\n", pPlayerGroup->GetGroupID(), pPlayerGroup->GetNumMembers() ); |
|
pPlayerGroup->GetSharedObject()->Dump(); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YieldingSessionStartPlaying( CGCUserSession *pSession ) |
|
{ |
|
mapMembersToPlayerGroups_t::IndexType_t nPlayerGroupIndex = m_mapMemberToGroup.Find( pSession->GetSteamID() ); |
|
if ( nPlayerGroupIndex == m_mapMemberToGroup.InvalidIndex() ) |
|
{ |
|
mapPlayersMemcacheJobCount_t::IndexType_t mIndex = sm_mapPlayersMemcacheJobCount.Find( pSession->GetSteamID() ); |
|
if ( mIndex == sm_mapPlayersMemcacheJobCount.InvalidIndex() ) |
|
{ |
|
sm_mapPlayersMemcacheJobCount.Insert( pSession->GetSteamID(), 1 ); |
|
} |
|
else |
|
{ |
|
sm_mapPlayersMemcacheJobCount.Element( mIndex )++; |
|
} |
|
|
|
// could not find a group in memory for this user, see if we can find it in memcached |
|
CGCJobFindGroupFromMemcached *pJob = new CGCJobFindGroupFromMemcached( GGCBase(), this, pSession->GetSteamID() ); |
|
pJob->StartJob( NULL ); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YieldingSessionStartServer( CGCGSSession *pSession ) |
|
{ |
|
} |
|
|
|
void CPlayerGroupManager::YieldingSessionStopServer( CGCGSSession *pSession ) |
|
{ |
|
} |
|
|
|
void CPlayerGroupManager::YldDoFindGroupFromMemcached( const CSteamID &memberSteamID ) |
|
{ |
|
// See if we can find him in memcached |
|
CUtlVector<CUtlString> memcachedMemberKeys; |
|
CUtlVector<CGCBase::GCMemcachedGetResult_t> memcachedMemberResults; |
|
CUtlString &memberKey = memcachedMemberKeys[ memcachedMemberKeys.AddToTail() ]; |
|
memberKey.Format( "GC:%u:%sMEMBER:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), memberSteamID.ConvertToUint64() ); |
|
|
|
if ( !GGCBase()->BYieldingMemcachedGet( memcachedMemberKeys, memcachedMemberResults ) ) |
|
{ |
|
return; // Not found |
|
} |
|
|
|
if ( memcachedMemberResults.Count() != 1 || !memcachedMemberResults[0].m_bKeyFound ) |
|
{ |
|
return; // Not found |
|
} |
|
CUtlBuffer memcachedGroupResult; |
|
memcachedGroupResult.SetExternalBuffer( memcachedMemberResults[0].m_bufValue.Base(), memcachedMemberResults[0].m_bufValue.Count(), memcachedMemberResults[0].m_bufValue.Count() ); |
|
PlayerGroupID_t nPlayerGroupID = (uint64) memcachedGroupResult.GetInt64(); |
|
|
|
if ( !BYieldingLockGroupID( nPlayerGroupID ) ) |
|
{ |
|
EmitInfo( SPEW_GC, SPEW_ALWAYS, LOG_ALWAYS, "YldFindGroupFromMemcached: Unable to create lock for PlayerGroup:%u\n", nPlayerGroupID ); |
|
return; |
|
} |
|
|
|
// check no-one else recreated this group while we were waiting for the lock |
|
if ( FindGroup( nPlayerGroupID ) ) |
|
return; |
|
|
|
// We have a group id, try read the group from memcached |
|
CUtlVector<CUtlString> memcachedGroupKeys; |
|
CUtlVector<CGCBase::GCMemcachedGetResult_t> memcachedGroupResults; |
|
CUtlString &memcachedGroupKey = memcachedGroupKeys[ memcachedGroupKeys.AddToTail() ]; |
|
memcachedGroupKey.Format( "GC:%u:%s:%llu", GGCBase()->GetAppID(), GetMemcachedIdentityKey(), nPlayerGroupID ); |
|
|
|
if ( !GGCBase()->BYieldingMemcachedGet( memcachedGroupKeys, memcachedGroupResults ) ) |
|
{ |
|
return; // Not found |
|
} |
|
|
|
if ( memcachedGroupResults.Count() != 1 || !memcachedGroupResults[0].m_bKeyFound ) |
|
{ |
|
return; // Not found |
|
} |
|
|
|
|
|
CUtlBuffer memcachedPartyResult; |
|
memcachedPartyResult.SetExternalBuffer( memcachedGroupResults[0].m_bufValue.Base(), memcachedGroupResults[0].m_bufValue.Count(), memcachedGroupResults[0].m_bufValue.Count() ); |
|
IPlayerGroup *pPlayerGroup = YldCreateAndLockPlayerGroupFromMemcached( memcachedPartyResult ); |
|
|
|
// pPlayerGroup won't be NULL because creating it won't fail. Also, the group id is already locked, so only one player |
|
// will get to this point. So no checks needed before inserting this group id into the map |
|
m_mapGroups.Insert( nPlayerGroupID, pPlayerGroup ); |
|
|
|
// Group loaded, update all objects |
|
|
|
// Add members to the map |
|
for ( int i = 0; i < pPlayerGroup->GetNumMembers(); ++i ) |
|
{ |
|
// It's ok to add the Nth steam id even with the yield in this loop, |
|
// because the group is locked. Any player that stops playing will be |
|
// blocked on this group id. |
|
CSteamID memberSteamID = pPlayerGroup->GetMember(i); |
|
m_mapMemberToGroup.InsertOrReplace( memberSteamID, pPlayerGroup ); |
|
|
|
CGCSharedObjectCache *pCache = GGCBase()->YieldingGetLockedSOCache( memberSteamID ); |
|
if ( pCache ) |
|
{ |
|
pCache->AddObject( pPlayerGroup->GetSharedObject() ); |
|
} |
|
GGCBase()->UnlockSteamID( memberSteamID ); |
|
} |
|
|
|
YldOnGroupLoadedFromMemcached( pPlayerGroup ); |
|
|
|
for ( int i = 0; i < pPlayerGroup->GetNumMembers(); i++ ) |
|
{ |
|
CGCUserSession *pMemberSession = GGCBase()->FindUserSession( pPlayerGroup->GetMember( i ) ); |
|
if ( !pMemberSession ) |
|
continue; |
|
|
|
pMemberSession->GetSOCache()->SendAllNetworkUpdates(); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YldFindGroupFromMemcached( const CSteamID &memberSteamID ) |
|
{ |
|
YldDoFindGroupFromMemcached( memberSteamID ); |
|
|
|
mapPlayersMemcacheJobCount_t::IndexType_t mIndex = sm_mapPlayersMemcacheJobCount.Find( memberSteamID ); |
|
if ( mIndex != sm_mapPlayersMemcacheJobCount.InvalidIndex() ) |
|
{ |
|
if ( --sm_mapPlayersMemcacheJobCount.Element( mIndex ) <= 0 ) |
|
{ |
|
sm_mapPlayersMemcacheJobCount.RemoveAt( mIndex ); |
|
} |
|
} |
|
} |
|
|
|
bool CGCJobDestroyGroup::BYieldingRunGCJob() |
|
{ |
|
if ( m_pGroupManager ) |
|
{ |
|
m_pGroupManager->YldDestroyGroup( m_nPlayerGroupID ); |
|
} |
|
return true; |
|
} |
|
|
|
bool CGCJobLeaveGroup::BYieldingRunGCJob() |
|
{ |
|
if ( m_pGroupManager ) |
|
{ |
|
m_pGroupManager->BYldRequestLeaveGroup( m_SteamID ); |
|
} |
|
return true; |
|
} |
|
|
|
bool CGCJobFindGroupFromMemcached::BYieldingRunGCJob() |
|
{ |
|
if ( m_pGroupManager ) |
|
{ |
|
m_pGroupManager->YldFindGroupFromMemcached( m_memberSteamID ); |
|
} |
|
return true; |
|
} |
|
|
|
|
|
// === Invites === |
|
|
|
|
|
void CPlayerGroupManager::YldInviteToGroup( const CSteamID &steamIDLeader, const CSteamID &steamIDNewMember ) |
|
{ |
|
if ( !steamIDLeader.IsValid() || ! steamIDLeader.BIndividualAccount() |
|
|| !steamIDNewMember.IsValid() || ! steamIDNewMember.BIndividualAccount() ) |
|
return; |
|
|
|
// create group if this leader isn't in one already |
|
IPlayerGroup *pPlayerGroup = YldFindAndLockGroupByMemberID( steamIDLeader ); |
|
if ( !pPlayerGroup ) |
|
{ |
|
pPlayerGroup = YldCreateAndLockPlayerGroup(); |
|
m_mapGroups.Insert( pPlayerGroup->GetGroupID(), pPlayerGroup ); |
|
if ( !BYldAddMemberToGroup( pPlayerGroup->GetGroupID(), steamIDLeader ) ) |
|
return; |
|
pPlayerGroup->SetLeader( steamIDLeader ); |
|
} |
|
else if ( pPlayerGroup && pPlayerGroup->GetLeader() != steamIDLeader ) |
|
{ |
|
// non-leader attempting to invite |
|
return; |
|
} |
|
|
|
// don't send any invite if our group is already full |
|
if ( GetMaxGroupMembers() != k_NoGroupMemberLimit && pPlayerGroup->GetNumMembers() >= GetMaxGroupMembers() ) |
|
{ |
|
CProtoBufMsg<CMsgGCError> msg( k_EMsgGCError ); |
|
msg.Body().set_error_text( "Your party is full." ); |
|
GGCBase()->BSendGCMsgToClient( steamIDLeader, msg ); |
|
return; |
|
} |
|
|
|
if ( pPlayerGroup->GetPendingInviteIndexBySteamID( steamIDNewMember ) == -1 ) |
|
{ |
|
pPlayerGroup->AddPendingInvite( steamIDNewMember ); |
|
} |
|
|
|
if ( !YldHasGroupInvite( steamIDNewMember, pPlayerGroup->GetGroupID() ) ) |
|
{ |
|
CGCSharedObjectCache *pCache = GGCBase()->YieldingGetLockedSOCache( steamIDNewMember ); |
|
if ( pCache ) |
|
{ |
|
// Send invite to the new member |
|
IPlayerGroupInvite *pInvite = CreateInvite(); |
|
pInvite->YldInitFromPlayerGroup( pPlayerGroup ); |
|
|
|
pCache->AddObject( pInvite->GetSharedObject() ); |
|
} |
|
GGCBase()->UnlockSteamID( steamIDNewMember ); |
|
} |
|
|
|
// Send a message back to the sender telling him his invitation was created |
|
GCSDK::CProtoBufMsg<CMsgInvitationCreated> msg( k_EMsgGCInvitationCreated ); |
|
msg.Body().set_group_id( pPlayerGroup->GetGroupID() ); |
|
msg.Body().set_steam_id( steamIDNewMember.ConvertToUint64() ); |
|
GGCBase()->BSendGCMsgToClient( steamIDLeader, msg ); |
|
|
|
SendGroupStorageAndNetworkUpdate( pPlayerGroup ); |
|
} |
|
|
|
void CPlayerGroupManager::YldRemovePendingInvite( PlayerGroupID_t nPlayerGroupID, const CSteamID &steamIDNewMember ) |
|
{ |
|
IPlayerGroup *pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( pPlayerGroup != NULL ) |
|
{ |
|
pPlayerGroup->RemovePendingInvite( steamIDNewMember ); |
|
UnlockGroupID( nPlayerGroupID ); |
|
} |
|
|
|
// Derived classes will remove the CPartyInvite/CLobbyInvite objects from the user's SO cache |
|
} |
|
|
|
void CPlayerGroupManager::YldGroupInviteResponse( const CSteamID &steamID, const PlayerGroupID_t nPlayerGroupID, bool bAccepted ) |
|
{ |
|
if ( !steamID.IsValid() || !steamID.BIndividualAccount() ) |
|
return; |
|
|
|
// check if user is already in a group |
|
if ( bAccepted ) |
|
{ |
|
IPlayerGroup* pOtherGroup = YldFindAndLockGroupByMemberID( steamID ); |
|
if ( pOtherGroup ) |
|
{ |
|
// remove him from the other group |
|
PlayerGroupID_t nOtherPlayerGroupID = pOtherGroup->GetGroupID(); |
|
YldRemoveMemberFromGroup( nOtherPlayerGroupID, steamID ); |
|
YldDestroyGroupIfEmpty( nOtherPlayerGroupID ); |
|
UnlockGroupID( nOtherPlayerGroupID ); |
|
|
|
// If the group wasn't deleted, update others about this person leaving |
|
pOtherGroup = FindGroup( nOtherPlayerGroupID ); |
|
if ( pOtherGroup ) |
|
{ |
|
SendGroupStorageAndNetworkUpdate( pOtherGroup ); |
|
} |
|
GJobCur().ReleaseLocks(); |
|
} |
|
GJobCur().ReleaseLocks(); |
|
} |
|
|
|
IPlayerGroup* pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( !pPlayerGroup ) |
|
{ |
|
CProtoBufMsg<CMsgGCError> msg( k_EMsgGCError ); |
|
msg.Body().set_error_text( CFmtStr( "Couldn't respond to party invite, couldn't find a party with ID %llu.", nPlayerGroupID ) ); |
|
GGCBase()->BSendGCMsgToClient( steamID, msg ); |
|
return; |
|
} |
|
|
|
// check an invite is pending for this user |
|
if ( pPlayerGroup->GetPendingInviteIndexBySteamID( steamID ) == -1 ) |
|
{ |
|
CProtoBufMsg<CMsgGCError> msg( k_EMsgGCError ); |
|
msg.Body().set_error_text( CFmtStr( "Couldn't respond to party invite, that party (%llu) doesn't have an invite pending for you.", nPlayerGroupID ) ); |
|
GGCBase()->BSendGCMsgToClient( steamID, msg ); |
|
return; |
|
} |
|
|
|
YldRemovePendingInvite( nPlayerGroupID, steamID ); |
|
|
|
if ( bAccepted ) |
|
{ |
|
// don't exceed the max size for this group |
|
if ( GetMaxGroupMembers() != k_NoGroupMemberLimit && pPlayerGroup->GetNumMembers() >= GetMaxGroupMembers() ) |
|
{ |
|
CProtoBufMsg<CMsgGCError> msg( k_EMsgGCError ); |
|
msg.Body().set_error_text( "Cannot join party. Party is full." ); |
|
GGCBase()->BSendGCMsgToClient( steamID, msg ); |
|
return; |
|
} |
|
|
|
BYldAddMemberToGroup( nPlayerGroupID, steamID ); |
|
} |
|
else |
|
{ |
|
// TODO: Praps notify the group that this person rejected the invite? |
|
} |
|
|
|
SendGroupStorageAndNetworkUpdate( pPlayerGroup ); |
|
} |
|
|
|
void CPlayerGroupManager::YldRequestKickFromGroup( const CSteamID &steamIDLeader, const CSteamID &steamIDVictim ) |
|
{ |
|
if ( !steamIDLeader.IsValid() || ! steamIDLeader.BIndividualAccount() |
|
|| !steamIDVictim.IsValid() || ! steamIDVictim.BIndividualAccount() ) |
|
return; |
|
|
|
// find leader's group |
|
IPlayerGroup* pPlayerGroup = YldFindAndLockGroupByMemberID( steamIDLeader ); |
|
if ( !pPlayerGroup ) |
|
return; |
|
|
|
if ( pPlayerGroup->GetLeader() != steamIDLeader || steamIDLeader == steamIDVictim ) |
|
{ |
|
// non-leader attempting to kick |
|
return; |
|
} |
|
|
|
// clear any pending invites for this person |
|
if ( pPlayerGroup->GetPendingInviteIndexBySteamID( steamIDVictim ) != -1 ) |
|
{ |
|
YldRemovePendingInvite( pPlayerGroup->GetGroupID(), steamIDVictim ); |
|
} |
|
|
|
PlayerGroupID_t nPlayerGroupID = pPlayerGroup->GetGroupID(); |
|
YldRemoveMemberFromGroup( nPlayerGroupID, steamIDVictim ); |
|
YldDestroyGroupIfEmpty( nPlayerGroupID ); |
|
|
|
pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( pPlayerGroup ) |
|
{ |
|
SendGroupStorageAndNetworkUpdate( pPlayerGroup ); |
|
} |
|
} |
|
|
|
bool CPlayerGroupManager::BYldRequestLeaveGroup( const CSteamID &steamID ) |
|
{ |
|
if ( !steamID.IsValid() || ! steamID.BIndividualAccount() ) |
|
return false; |
|
|
|
// find group |
|
IPlayerGroup *pPlayerGroup = YldFindAndLockGroupByMemberID( steamID ); |
|
if ( !pPlayerGroup ) |
|
{ |
|
CProtoBufMsg<CMsgGCError> msg( k_EMsgGCError ); |
|
msg.Body().set_error_text( CFmtStr( "%s: Failed to remove you from your group. Couldn't find your group in the group map.", GetMemcachedIdentityKey() ) ); |
|
GGCBase()->BSendGCMsgToClient( steamID, msg ); |
|
return false; |
|
} |
|
|
|
// clear any pending invites for this person |
|
if ( pPlayerGroup->GetPendingInviteIndexBySteamID( steamID ) != -1 ) |
|
{ |
|
YldRemovePendingInvite( pPlayerGroup->GetGroupID(), steamID ); |
|
} |
|
|
|
PlayerGroupID_t nPlayerGroupID = pPlayerGroup->GetGroupID(); |
|
YldRemoveMemberFromGroup( nPlayerGroupID, steamID ); |
|
YldDestroyGroupIfEmpty( nPlayerGroupID ); |
|
|
|
pPlayerGroup = YldFindAndLockGroup( nPlayerGroupID ); |
|
if ( pPlayerGroup ) |
|
{ |
|
SendGroupStorageAndNetworkUpdate( pPlayerGroup ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CPlayerGroupManager::YldOnPlayerLeftGroup( IPlayerGroup *pPlayerGroup, const CSteamID& steamIDRemovingMember ) |
|
{ |
|
// if the group leader just left, then set a new leader |
|
if ( pPlayerGroup->GetLeader() == steamIDRemovingMember && pPlayerGroup->GetNumMembers() > 0 ) |
|
{ |
|
pPlayerGroup->SetLeader( pPlayerGroup->GetMember( 0 ) ); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YldOnGroupDestroyed( IPlayerGroup *pPlayerGroup ) |
|
{ |
|
PlayerGroupID_t nPlayerGroupID = pPlayerGroup->GetGroupID(); |
|
|
|
// remove all pending invites |
|
EmitInfo( SPEW_GC, 4, LOG_ALWAYS, "CPlayerGroupManager::YldOnGroupDestroyed [%s] Removing all pending invites %016llx\n", GetMemcachedIdentityKey(), nPlayerGroupID ); |
|
for ( int i = pPlayerGroup->GetNumPendingInvites() - 1; i >= 0; i-- ) // iterate backwards as pending invites will be removed from the list |
|
{ |
|
YldRemovePendingInvite( nPlayerGroupID, pPlayerGroup->GetPendingInvite( i ) ); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YldCreateInvitesForGroup( PlayerGroupID_t nPlayerGroupID ) |
|
{ |
|
// Check to see if the group was loaded, if not discard, it should always be loaded first |
|
IPlayerGroup *pPlayerGroup = FindGroup( nPlayerGroupID ); |
|
if ( !pPlayerGroup ) |
|
return; |
|
|
|
for ( int i = 0; i < pPlayerGroup->GetNumPendingInvites(); ++i ) |
|
{ |
|
CSteamID steamIDNewMember = pPlayerGroup->GetPendingInvite(i); |
|
|
|
const char *szAccountName = "Unknown Player"; |
|
CAccountDetails *pDetails = GGCBase()->YieldingGetAccountDetails( pPlayerGroup->GetLeader() ); |
|
if ( pDetails ) |
|
{ |
|
szAccountName = pDetails->GetPersonaName(); |
|
} |
|
|
|
// Send invite to the new member |
|
IPlayerGroupInvite *pInvite = CreateInvite(); |
|
pInvite->SetGroupID( pPlayerGroup->GetGroupID() ); |
|
pInvite->SetSenderID( pPlayerGroup->GetLeader().ConvertToUint64() ); |
|
pInvite->SetSenderName( szAccountName ); |
|
CGCSharedObjectCache *pCache = GGCBase()->YieldingGetLockedSOCache( steamIDNewMember ); |
|
if ( pCache ) |
|
{ |
|
pCache->AddObject( pInvite->GetSharedObject() ); |
|
} |
|
GGCBase()->UnlockSteamID( steamIDNewMember ); |
|
} |
|
} |
|
|
|
void CPlayerGroupManager::YldOnGroupLoadedFromMemcached( GCSDK::IPlayerGroup *pPlayerGroup ) |
|
{ |
|
YldCreateInvitesForGroup( pPlayerGroup->GetGroupID() ); |
|
} |
|
|
|
bool CPlayerGroupManager::IsPlayerWaitingForMemcache( const CSteamID &steamID ) const |
|
{ |
|
mapPlayersMemcacheJobCount_t::IndexType_t mIndex = sm_mapPlayersMemcacheJobCount.Find( steamID ); |
|
if ( mIndex != sm_mapPlayersMemcacheJobCount.InvalidIndex() ) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|