From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- gcsdk/gcclient_sharedobjectcache.cpp | 586 +++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 gcsdk/gcclient_sharedobjectcache.cpp (limited to 'gcsdk/gcclient_sharedobjectcache.cpp') diff --git a/gcsdk/gcclient_sharedobjectcache.cpp b/gcsdk/gcclient_sharedobjectcache.cpp new file mode 100644 index 0000000..506ca44 --- /dev/null +++ b/gcsdk/gcclient_sharedobjectcache.cpp @@ -0,0 +1,586 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Extra functionality on top of CGCClientSharedObjectCache for GCClients +// +//============================================================================= + +#include "stdafx.h" +#include +#include "gcsdk/gcclient_sharedobjectcache.h" +#include "gcsdk_gcmessages.pb.h" +#include + +namespace GCSDK +{ + +//#define SOCDebug(...) Msg( __VA_ARGS__ ) +#define SOCDebug(...) ((void)0) + +//---------------------------------------------------------------------------- +// Purpose: constructor +//---------------------------------------------------------------------------- +CGCClientSharedObjectContext::CGCClientSharedObjectContext( const CSteamID & steamIDOwner ) + : m_steamIDOwner( steamIDOwner ) +{ + +} + + +//---------------------------------------------------------------------------- +// Purpose: Adds a new Listener to the cache. All objects in the cache will +// be sent as create messages to the new Listener +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectContext::BAddListener( ISharedObjectListener *pListener ) +{ + if( m_vecListeners.HasElement( pListener ) ) + return false; + + m_vecListeners.AddToTail( pListener ); + return true; +} + + +//---------------------------------------------------------------------------- +// Purpose: Removes a Listener from the cache. All objects in the cache +// will have destroy messages sent for them to the new Listener. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectContext::BRemoveListener( ISharedObjectListener *pListener ) +{ + return m_vecListeners.FindAndRemove( pListener ); +} + + +//---------------------------------------------------------------------------- +// Purpose: Send created/updated/destroyed calls on to all the listeners in the +// context +//---------------------------------------------------------------------------- +void CGCClientSharedObjectContext::SOCreated( const CSharedObject *pObject, ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->SOCreated( m_steamIDOwner, pObject, eEvent ); + } +} + +void CGCClientSharedObjectContext::PreSOUpdate( ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->PreSOUpdate( m_steamIDOwner, eEvent ); + } +} + +void CGCClientSharedObjectContext::SOUpdated( const CSharedObject *pObject, ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->SOUpdated( m_steamIDOwner, pObject, eEvent ); + } +} + +void CGCClientSharedObjectContext::PostSOUpdate( ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->PostSOUpdate( m_steamIDOwner, eEvent ); + } +} + +void CGCClientSharedObjectContext::SODestroyed( const CSharedObject *pObject, ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->SODestroyed( m_steamIDOwner, pObject, eEvent ); + } +} + +void CGCClientSharedObjectContext::SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->SOCacheSubscribed( steamIDOwner, eEvent ); + } +} + +void CGCClientSharedObjectContext::SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) const +{ + tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ ); + FOR_EACH_VEC( m_vecListeners, nListener ) + { + m_vecListeners[nListener]->SOCacheUnsubscribed( steamIDOwner, eEvent ); + } +} + + +//---------------------------------------------------------------------------- +// Purpose: Constructor +//---------------------------------------------------------------------------- +CGCClientSharedObjectTypeCache::CGCClientSharedObjectTypeCache( int nTypeID, const CGCClientSharedObjectContext & context ) + : m_context( context ), CSharedObjectTypeCache( nTypeID ) +{ + +} + + +//---------------------------------------------------------------------------- +// Purpose: Destructor +//---------------------------------------------------------------------------- +CGCClientSharedObjectTypeCache::~CGCClientSharedObjectTypeCache() +{ +} + + +//---------------------------------------------------------------------------- +// Purpose: Parses a cache subscribed message. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectTypeCache::BParseCacheSubscribedMsg( const CMsgSOCacheSubscribed_SubscribedType & msg, CUtlVector &vecCreatedObjects, CUtlVector &vecUpdatedObjects, CUtlVector &vecObjectsToDestroy ) +{ + CSharedObjectVec vecUntouchedObjects; + for ( uint32 i = 0; i < GetCount(); i++ ) + { + vecUntouchedObjects.AddToTail( GetObject( i ) ); + } + + for( uint16 usObject = 0; usObject < msg.object_data_size(); usObject++ ) + { + bool bUpdatedExisting = false; + CSharedObject *pObject = BCreateFromMsg( msg.object_data( usObject ).data(), msg.object_data( usObject ).size(), &bUpdatedExisting ); + if ( pObject == NULL) + { + Assert( pObject ); + return false; + } + + // if an object was updated, remove it from the untouched list + if ( bUpdatedExisting ) + { + int index = vecUntouchedObjects.Find( pObject ); + if ( index != vecUntouchedObjects.InvalidIndex() ) + { + vecUntouchedObjects[index] = NULL; + } + vecUpdatedObjects.AddToTail( pObject ); + } + else + { + vecCreatedObjects.AddToTail( pObject ); + } + } + + // all objects that weren't in the SubscribedMsg should be destroyed + for ( int i = 0; i < vecUntouchedObjects.Count(); i++ ) + { + if ( vecUntouchedObjects[i] == NULL ) + continue; + + CSharedObject *pObject = RemoveObject( *vecUntouchedObjects[i] ); + Assert( pObject ); + if( pObject ) + vecObjectsToDestroy.AddToTail( pObject ); + } + + return true; +} + +void CGCClientSharedObjectTypeCache::RemoveAllObjects( CUtlVector &vecObjects ) +{ + + // Go in reverse order to avoid O(n^2) shifting the items in the array + for ( int i = GetCount() - 1; i >= 0; i-- ) + { + CSharedObject *pObject = RemoveObjectByIndex( i ); + Assert( pObject ); + if ( pObject ) + vecObjects.AddToTail( pObject ); + } +} + + +//---------------------------------------------------------------------------- +// Purpose: Processes a received create message for an object of this type on +// the client/gameserver +//---------------------------------------------------------------------------- +CSharedObject *CGCClientSharedObjectTypeCache::BCreateFromMsg( const void *pvData, uint32 unSize, bool *bUpdatedExisting ) +{ + CUtlBuffer bufCreate( pvData, unSize, CUtlBuffer::READ_ONLY ); + CSharedObject *pNewObj = CSharedObject::Create( GetTypeID() ); + Assert( pNewObj ); + if( !pNewObj ) + { + EmitError( SPEW_SHAREDOBJ, "Unable to create object of type %d\n", GetTypeID() ); + return NULL; + } + + if( !pNewObj->BParseFromMessage( bufCreate ) ) + { + delete pNewObj; + return NULL; + } + + // Existing object? + CSharedObject *pObj = FindSharedObject( *pNewObj ); + if( pObj ) + { + pObj->Copy( *pNewObj ); + delete pNewObj; + if ( bUpdatedExisting ) + { + *bUpdatedExisting = true; + } + return pObj; + } + + // New object + AddObject( pNewObj ); + if ( bUpdatedExisting ) + { + *bUpdatedExisting = false; + } + return pNewObj; +} + + +//---------------------------------------------------------------------------- +// Purpose: Processes a received destroy message for an object of this type on +// the client/gameserver +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectTypeCache::BDestroyFromMsg( const void *pvData, uint32 unSize ) +{ + CUtlBuffer bufDestroy( pvData, unSize, CUtlBuffer::READ_ONLY ); + CSharedObject *pIndexObj = CSharedObject::Create( GetTypeID() ); + if( !pIndexObj->BParseFromMessage( bufDestroy ) ) + { + delete pIndexObj; + return false; + } + + CSharedObject *pObject = RemoveObject( *pIndexObj ); + if( pObject ) + { + m_context.SODestroyed( pObject, eSOCacheEvent_Incremental ); + delete pObject; + } + + delete pIndexObj; + return true; +} + + +//---------------------------------------------------------------------------- +// Purpose: Processes a received destroy message for an object of this type on +// the client/gameserver +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectTypeCache::BUpdateFromMsg( const void *pvData, uint32 unSize ) +{ + CUtlBuffer bufUpdate( pvData, unSize, CUtlBuffer::READ_ONLY ); + CSharedObject *pIndexObj = CSharedObject::Create( GetTypeID() ); + AssertMsg1( pIndexObj, "Unable to create index object of type %d", GetTypeID() ); + if( !pIndexObj ) + return false; + if( !pIndexObj->BParseFromMessage( bufUpdate ) ) + { + delete pIndexObj; + return false; + } + + CSharedObject *pObj = FindSharedObject( *pIndexObj ); + bool bRet = false; + if( pObj ) + { + bufUpdate.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + bRet = pObj->BUpdateFromNetwork( *pIndexObj ); + m_context.SOUpdated( pObj, eSOCacheEvent_Incremental ); + } + + delete pIndexObj; + return bRet; +} + + +//---------------------------------------------------------------------------- +// Purpose: Constructor +//---------------------------------------------------------------------------- +CGCClientSharedObjectCache::CGCClientSharedObjectCache( const CSteamID & steamIDOwner ) + : m_context( steamIDOwner ), + m_bInitialized( false ), + m_bSubscribed( false ) +{ + +} + + +//---------------------------------------------------------------------------- +// Purpose: Destructor +//---------------------------------------------------------------------------- +CGCClientSharedObjectCache::~CGCClientSharedObjectCache() +{ +} + + +//---------------------------------------------------------------------------- +// Purpose: Process an incoming create message on a client/gameserver. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectCache::BParseCacheSubscribedMsg( const CMsgSOCacheSubscribed & msg ) +{ + + // Assume all type caches will be untouched + CUtlVector vecUntouchedTypes; + for ( int i = FirstTypeCacheIndex(); i != InvalidTypeCacheIndex(); i = NextTypeCacheIndex( i ) ) + { + CSharedObjectTypeCache *pTypeCache = GetTypeCacheByIndex( i ); + if ( pTypeCache ) + { + vecUntouchedTypes.AddToTail( pTypeCache->GetTypeID() ); + } + } + + // List of objects created, updated, and removed + CUtlVector vecCreatedObjects; + CUtlVector vecUpdatedObjects; + CUtlVector vecObjectsToDestroy; + + bool bResult = true; + + // Scan types in message + for( uint16 usObject = 0; usObject < msg.objects_size(); usObject++ ) + { + const CMsgSOCacheSubscribed_SubscribedType & msgType = msg.objects( usObject ); + + // Find or create the type + CGCClientSharedObjectTypeCache *pTypeCache = CreateTypeCache( msgType.type_id() ); + if ( pTypeCache ) + { + int index = vecUntouchedTypes.Find( pTypeCache->GetTypeID() ); + if ( index != vecUntouchedTypes.InvalidIndex() ) + { + vecUntouchedTypes[index] = -1; + } + } + Assert( pTypeCache ); + if( !pTypeCache || !pTypeCache->BParseCacheSubscribedMsg( msgType, vecCreatedObjects, vecUpdatedObjects, vecObjectsToDestroy ) ) + bResult = false; + } + + // any type caches that weren't in the SubscribedMsg should be cleared + for ( int i = FirstTypeCacheIndex(); i != InvalidTypeCacheIndex(); i = NextTypeCacheIndex( i ) ) + { + CGCClientSharedObjectTypeCache *pTypeCache = GetTypeCacheByIndex( i ); + if ( vecUntouchedTypes.Find( pTypeCache->GetTypeID() ) != vecUntouchedTypes.InvalidIndex() ) + { + pTypeCache->RemoveAllObjects( vecObjectsToDestroy ); + } + } + + // Which event is happening? + ESOCacheEvent eNotificationEvent = eSOCacheEvent_Subscribed; + if ( m_bSubscribed ) + eNotificationEvent = eSOCacheEvent_Resubscribed; + + // Set version, assuming we didn't have any problems. If we hit any problems, + // we want to force a refresh + if ( bResult ) + SetVersion( msg.version() ); + + // Mark that the cache has been initialized by the server + m_bInitialized = true; + m_bSubscribed = true; + + // + // Send notifications + // + + // Initial cache subscribed + m_context.SOCacheSubscribed( GetOwner(), eNotificationEvent ); + + // Deletions + for ( int i = 0 ; i < vecObjectsToDestroy.Count() ; ++i ) + { + m_context.SODestroyed( vecObjectsToDestroy[i], eNotificationEvent ); + delete vecObjectsToDestroy[i]; + } + + // Updates + for ( int i = 0 ; i < vecUpdatedObjects.Count() ; ++i ) + { + m_context.SOUpdated( vecUpdatedObjects[i], eNotificationEvent ); + } + + // Created + for ( int i = 0 ; i < vecCreatedObjects.Count() ; ++i ) + { + m_context.SOUpdated( vecCreatedObjects[i], eNotificationEvent ); + } + + // Return true if everything parsed OK, or false + // if we had at least one failure + return bResult; +} + + +//---------------------------------------------------------------------------- +// Purpose: Process an incoming create message on a client/gameserver. +//---------------------------------------------------------------------------- +void CGCClientSharedObjectCache::NotifyUnsubscribe() +{ + if ( m_bSubscribed ) + { + m_bSubscribed = false; + m_context.SOCacheUnsubscribed( GetOwner(), eSOCacheEvent_Unsubscribed ); + } + else + { + AssertMsg( m_bSubscribed, "GC Sending us Unsubscribed message when we weren't subscribed" ); // Might not be a bug, but something worth checking + } +} + +//---------------------------------------------------------------------------- +// Purpose: GC is telling us that the version we have is up-to-date,a nd we are subscribed +//---------------------------------------------------------------------------- +void CGCClientSharedObjectCache::NotifyResubscribedUpToDate() +{ + if ( !m_bSubscribed ) + { + Assert( m_bInitialized ); + m_bSubscribed = true; + m_context.SOCacheSubscribed( GetOwner(), eSOCacheEvent_Subscribed ); + } + else + { + AssertMsg( m_bSubscribed, "Got NotifyResubscribedUpToDate when we were already subscribed?" ); // Might not be a bug, but something worth checking + } +} + + +//---------------------------------------------------------------------------- +// Purpose: Process an incoming create message on a client/gameserver. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectCache::BCreateFromMsg( int nTypeID, const void *pvData, uint32 unSize ) +{ + // We should be subscribed + if ( !m_bInitialized || !m_bSubscribed ) + { + // Note: We can go down and come back up without the GC knowing this. + // So this can happen + //Assert( m_bInitialized ); + //Assert( m_bSubscribed ); + //EmitWarning( SPEW_SHAREDOBJ, 1, "Received SOCache incremental update for cache we were not subscribed to (object type %d)\n", nTypeID ); + } + + // Locate / create the type cache + CGCClientSharedObjectTypeCache *pTypeCache = CreateTypeCache( nTypeID ); + + // Create the message or update existing + bool bUpdatedExisting = false; + CSharedObject *pObject = pTypeCache->BCreateFromMsg( pvData, unSize, &bUpdatedExisting ); + if ( pObject == NULL ) + return false; + + // Send notifications to listeners + if ( bUpdatedExisting ) + { + // This can happen --- see comment at the top of this function + //Assert( !bUpdatedExisting ); // shouldn't the GC know what it's already sent us? This is weird + m_context.SOUpdated( pObject, eSOCacheEvent_Incremental ); + } + else + { + m_context.SOCreated( pObject, eSOCacheEvent_Incremental ); + } + + return true; +} + + +//---------------------------------------------------------------------------- +// Purpose: Processes an incoming destroy message on a client/gameserver. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectCache::BDestroyFromMsg( int nTypeID, const void *pvData, uint32 unSize ) +{ + CGCClientSharedObjectTypeCache *pTypeCache = FindTypeCache( nTypeID ); + if( pTypeCache ) + { + return pTypeCache->BDestroyFromMsg( pvData, unSize ); + } + else + { + return false; + } +} + +//---------------------------------------------------------------------------- +// Purpose: Processes an incoming update message on a client/gameserver. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectCache::BUpdateFromMsg( int nTypeID, const void *pvData, uint32 unSize ) +{ + CGCClientSharedObjectTypeCache *pTypeCache = FindTypeCache( nTypeID ); + if( pTypeCache ) + { + return pTypeCache->BUpdateFromMsg( pvData, unSize ); + } + else + { + return false; + } +} + +//---------------------------------------------------------------------------- +// Purpose: Adds a listener object to be notified of object changes in this +// cache. The shared object cache does not own this object and will +// not free it. +//---------------------------------------------------------------------------- +void CGCClientSharedObjectCache::AddListener( ISharedObjectListener *pListener ) +{ + Assert( pListener ); + if ( !m_context.BAddListener( pListener ) ) + return; // was already listening, no action needed + + SOCDebug( "[%s] Adding listener %s\n", GetOwner().Render(), typeid( *pListener ).name() ); + + // If we're already subscribed, then immediately send notifications + if( BIsSubscribed() ) + { + pListener->SOCacheSubscribed( GetOwner(), eSOCacheEvent_ListenerAdded ); + for ( int i = FirstTypeCacheIndex(); i != InvalidTypeCacheIndex(); i = NextTypeCacheIndex( i ) ) + { + CGCClientSharedObjectTypeCache *pTypeCache = GetTypeCacheByIndex( i ); + for ( uint32 j = 0 ; j < pTypeCache->GetCount() ; ++j ) + { + CSharedObject *pObject = pTypeCache->GetObject( j ); + pListener->SOCreated( GetOwner(), pObject, eSOCacheEvent_ListenerAdded ); + } + } + } +} + + +//---------------------------------------------------------------------------- +// Purpose: Removes a listener object from the list to be notified of changes +// to this object cache. +//---------------------------------------------------------------------------- +bool CGCClientSharedObjectCache::RemoveListener( ISharedObjectListener *pListener ) +{ + Assert( pListener ); + if ( !m_context.BRemoveListener( pListener ) ) + return false; // wasn't already listening, nothing to do + + SOCDebug( "[%s] Removing listener %s\n", GetOwner().Render(), typeid( *pListener ).name() ); + + // If we were subscribed, then the listener's last subscribe notification + // was a "you are subscribed." Send him an unsubscribed notification + // so he doesn't think he's still subscribed. + if( BIsSubscribed() ) + { + pListener->SOCacheUnsubscribed( GetOwner(), eSOCacheEvent_ListenerRemoved ); + } + + return true; +} + +} // namespace GCSDK -- cgit v1.2.3