diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/gcsdk/gc_sharedobjectcache.h | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/gcsdk/gc_sharedobjectcache.h')
| -rw-r--r-- | public/gcsdk/gc_sharedobjectcache.h | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/public/gcsdk/gc_sharedobjectcache.h b/public/gcsdk/gc_sharedobjectcache.h new file mode 100644 index 0000000..e7cdfc4 --- /dev/null +++ b/public/gcsdk/gc_sharedobjectcache.h @@ -0,0 +1,409 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Additional shared object cache functionality for the GC +// +//============================================================================= + +#ifndef GC_SHAREDOBJECTCACHE_H +#define GC_SHAREDOBJECTCACHE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "sharedobjectcache.h" +#include "tier1/utlhashtable.h" +#include "gcdirtyfield.h" +#include "gcsdk/gcsystemaccess.h" + +class CMsgSOCacheSubscribed_SubscribedType; + +#include "tier0/memdbgon.h" + +namespace GCSDK +{ + +class CCachedSubscriptionMessage; + +//---------------------------------------------------------------------------- +// Purpose: The part of a shared object cache that handles all objects of a +// single type. +//---------------------------------------------------------------------------- +class CSharedObjectContext +{ +public: + + //holds information about a subscriber to the context + struct Subscriber_t + { + CSteamID m_steamID; //the steam ID of the subscriber + int m_nRefCount; //the number of references to this subscription + }; + + CSharedObjectContext( const CSteamID & steamIDOwner ); + + bool BAddSubscriber( const CSteamID & steamID ); + bool BRemoveSubscriber( const CSteamID & steamID ); + void RemoveAllSubscribers(); + bool BIsSubscribed( const CSteamID & steamID ) const { return FindSubscriber( steamID ) != m_vecSubscribers.InvalidIndex(); } + + const CUtlVector< Subscriber_t > & GetSubscribers() const { return m_vecSubscribers; } + const CSteamID & GetOwner() const { return m_steamIDOwner; } + +private: + + //finds a steam ID within the subscriber list, returns the index, invalid if it can't be found + int FindSubscriber( const CSteamID& steamID ) const; + + CUtlVector<Subscriber_t> m_vecSubscribers; + CSteamID m_steamIDOwner; +}; + +class CGCSharedObjectTypeCache; + +enum ESOTypeFlags +{ + k_ESOFlag_SendToNobody = 0, + k_ESOFlag_SendToOwner = 1 << 0, // will go to the owner of the cache (user or gameserver), if he is subscribed + k_ESOFlag_SendToOtherUsers = 1 << 1, // will go to subscribed users who are not the owner of the cache + k_ESOFlag_SendToOtherGameservers = 1 << 2, // will go to subscribed gameservers who are not the owner of the cache + k_ESOFlag_SendToQuestObjectiveTrackers = 1 << 3, + k_ESOFlag_LastFlag = k_ESOFlag_SendToQuestObjectiveTrackers, +}; + +//---------------------------------------------------------------------------- +// Purpose: Filter object used to determine whether a type cache's objects should +// should be sent to subscribers and whether each object should be sent +//---------------------------------------------------------------------------- +class CISubscriberMessageFilter +{ +public: + virtual bool BShouldSendAnyObjectsInCache( CGCSharedObjectTypeCache *pTypeCache, uint32 unFlags ) const = 0; + virtual bool BShouldSendObject( CSharedObject *pSharedObject, uint32 unFlags ) const = 0; +}; + + +//---------------------------------------------------------------------------- +// Purpose: The part of a shared object cache that handles all objects of a +// single type. +//---------------------------------------------------------------------------- +class CGCSharedObjectTypeCache : public CSharedObjectTypeCache +{ +public: + + typedef CSharedObjectTypeCache Base; + + CGCSharedObjectTypeCache( int nTypeID, const CSharedObjectContext & context ); + virtual ~CGCSharedObjectTypeCache(); + + virtual bool AddObject( CSharedObject *pObject ); + virtual CSharedObject *RemoveObject( const CSharedObject & soIndex ); + + inline CSteamID GetOwner() const { return m_context.GetOwner(); } + + void BuildCacheSubscribedMsg( CMsgSOCacheSubscribed_SubscribedType *pMsgType, uint32 unFlags, const CISubscriberMessageFilter &filter ); + virtual void EnsureCapacity( uint32 nItems ); + +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, const char *pchName ); +#endif + +private: + + const CSharedObjectContext & m_context; +}; + + + +//---------------------------------------------------------------------------- +// Purpose: A cache of a bunch of shared objects of different types. This class +// is shared between clients, gameservers, and the GC and is +// responsible for sending messages from the GC to cause object +// creation/destruction/updating on the clients/gameservers. +//---------------------------------------------------------------------------- + +class CGCSharedObjectCache : public CSharedObjectCache +{ +public: + CGCSharedObjectCache( const CSteamID & steamIDOwner = CSteamID() ); + virtual ~CGCSharedObjectCache(); + + const CSteamID & GetOwner() const { return m_context.GetOwner(); } + const CUtlVector< CSharedObjectContext::Subscriber_t > & GetSubscribers() const { return m_context.GetSubscribers(); } + + CGCSharedObjectTypeCache *FindTypeCache( int nClassID ) const { return (CGCSharedObjectTypeCache *)FindBaseTypeCache( nClassID ); } + CGCSharedObjectTypeCache *CreateTypeCache( int nClassID ) { return (CGCSharedObjectTypeCache *)CreateBaseTypeCache( nClassID ); } + + virtual uint32 CalcSendFlags( const CSteamID &steamID ) const; + virtual const CISubscriberMessageFilter &GetSubscriberMessageFilter(); + virtual bool AddObject( CSharedObject *pSharedObject ); + virtual bool AddObjectClean( CSharedObject *pSharedObject ); + + bool BDestroyObject( const CSharedObject & soIndex, bool bRemoveFromDatabase ); + virtual CSharedObject *RemoveObject( const CSharedObject & soIndex ); + + template< typename SOClass_t > + bool BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults ); + + template< typename SOClass_t > + bool BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults ); + + template< typename SOClass_t, typename SchClass_t > + bool BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead ); + + template< typename SOClass_t, typename SchClass_t > + bool BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults ); + + // @todo temporary for trading and item subscriptions (to be removed once we get cross-game trading) + virtual void SetTradingPartner( const CSteamID &steamID ); + const CSteamID &GetTradingPartner() const { return m_steamIDTradingPartner; } + + void AddSubscriber( const CSteamID & steamID, bool bForceSendSubscriptionMsg = false ); + void RemoveSubscriber( const CSteamID & steamID ); + void RemoveAllSubscribers(); + void SendSubscriberMessage( const CSteamID & steamID ); + bool BIsSubscribed( const CSteamID & steamID ) { return m_context.BIsSubscribed( steamID ); } + void ClearCachedSubscriptionMessage(); + + bool BIsDatabaseDirty() const { return m_databaseDirtyList.NumDirtyObjects() > 0; } + + // This will mark the field as dirty for both network and database + void DirtyObjectField( CSharedObject *pObj, int nFieldIndex ); + + // Marks only dirty for network + void DirtyNetworkObject( CSharedObject *pObj ); + void DirtyNetworkObjectCreate( CSharedObject *pObj ); + + // Mark dirty for database + void DirtyDatabaseObjectField( CSharedObject *pObj, int nFieldIndex ); + + void SendNetworkUpdates( CSharedObject *pObj ); + + // Add a specific object write to a transaction. The cache is expected to remain locked until this transaction is + // closed. If the transaction is rolled back, the object will be returned to the dirty list. + bool BYieldingAddWriteToTransaction( CSharedObject *pObj, CSQLAccess & sqlAccess ); + + // Add all pending object writes to a transaction. The cache is expected to remain locked until this transaction is + // closed. If the transaction successfully commits, the dirty list will be flushed. + // + // This is intended for use in writeback -- no changes to the dirty objects list may occur until this transaction + // closes. + uint32 YieldingStageAllWrites( CSQLAccess & sqlAccess ); + + void SendAllNetworkUpdates(); + void FlushInventoryCache(); + void YieldingWriteToDatabase( CSharedObject *pObj ); + + void SetInWriteback( bool bInWriteback ); + bool GetInWriteback() const { return m_bInWriteback; } + RTime32 GetWritebackTime() const { return m_unWritebackTime; } + + void SetLRUHandle( uint32 unLRUHandle ) { m_unLRUHandle = unLRUHandle; } + uint32 GetLRUHandle() const { return m_unLRUHandle; } + + void Dump() const; + void DumpDirtyObjects() const; +#ifdef DBGFLAG_VALIDATE + virtual void Validate( CValidator &validator, const char *pchName ); +#endif + + bool IsObjectCached( const CSharedObject *pObj ) const { return IsObjectCached( pObj, pObj->GetTypeID() ); } + //the same as the above, but takes in the type since if called from a destructor, you can't use virtual functions + bool IsObjectCached( const CSharedObject *pObj, uint32 nTypeID ) const; + bool IsObjectDirty( const CSharedObject *pObj ) const; + + //called to mark that we are no longer loading + void SetDetectVersionChanges( bool bState ) { m_bDetectVersionChanges = bState; } + +protected: + + virtual CSharedObjectTypeCache *AllocateTypeCache( int nClassID ) const OVERRIDE { return new CGCSharedObjectTypeCache( nClassID, m_context ); } + + virtual void MarkDirty(); + virtual bool BShouldSendToAnyClients( uint32 unFlags ) const; + CCachedSubscriptionMessage *BuildSubscriberMessage( uint32 unFlags ); + + CSteamID m_steamIDTradingPartner; + +protected: + void SendNetworkCreateInternal( CSharedObject * pObj ); + void SendNetworkUpdateInternal( CSharedObject * pObj ); + void SendUnsubscribeMessage( const CSteamID & steamID ); + + //this is a flag that when set will cause any version changes to trigger an assert. This can be used during times like loading to ensure we don't have inappropriate version changes which + //can cause inefficiencies + bool m_bDetectVersionChanges; + + CSharedObjectContext m_context; + CUtlHashtable< CSharedObject * > m_networkDirtyObjs; + CUtlHashtable< CSharedObject * > m_networkDirtyObjsCreate; + CSharedObjectDirtyList m_databaseDirtyList; + bool m_bInWriteback; + RTime32 m_unWritebackTime; + uint32 m_unLRUHandle; + uint32 m_unCachedSubscriptionMsgFlags; + CCachedSubscriptionMessage *m_pCachedSubscriptionMsg; +}; + + + + +//---------------------------------------------------------------------------- +// Purpose: Loads a list of CSchemaSharedObjects from a result list from a +// query. +// Inputs: pResultSet - The result set from the SQL query +// schDefaults - A schema object that defines the values to set in +// the new objects for fields that were not read in the query. +// Typically this will be whatever fields were in the WHERE +// clause of the query. +// csRead - A columnSet defining the fields that were read in the query. +//---------------------------------------------------------------------------- +template< typename SOClass_t > +bool CGCSharedObjectCache::BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults ) +{ + if ( NULL == pResultSet ) + return false; + + //don't bother creating a cache if we don't have objects to add into it + if( pResultSet->GetRowCount() > 0 ) + { + CGCSharedObjectTypeCache *pTypeCache = CreateTypeCache( SOClass_t::k_nTypeID ); + pTypeCache->EnsureCapacity( pResultSet->GetRowCount() ); + for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() ) + { + SOClass_t *pObj = new SOClass_t(); + pObj->Obj() = objDefaults.Obj(); + record.BWriteToRecord( &pObj->Obj(), csRead ); + pTypeCache->AddObjectClean( pObj ); + } + } + + return true; +} + +//---------------------------------------------------------------------------- +// Purpose: Loads a single object of a type. If the object is not available, +// a new object will be created at default values +// Inputs: pResultSet - The result set from the SQL query +// schDefaults - A schema object that defines the values to set in +// the new objects for fields that were not read in the query. +// Typically this will be whatever fields were in the WHERE +// clause of the query. +// csRead - A columnSet defining the fields that were read in the query. +//---------------------------------------------------------------------------- +template< typename SOClass_t > +bool CGCSharedObjectCache::BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults ) +{ + if ( NULL == pResultSet ) + return false; + + if ( pResultSet->GetRowCount() > 1 ) + { + EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadSchSingleton() on type %d\n", objDefaults.GetTypeID() ); + return false; + } + else if ( pResultSet->GetRowCount() == 1 ) + { + return BYieldingLoadSchObjects<SOClass_t>( pResultSet, csRead, objDefaults ); + } + else + { + // Create it if there wasn't one + SOClass_t *pSchObj = new SOClass_t(); + pSchObj->Obj() = objDefaults.Obj(); + if( !pSchObj->BYieldingAddToDatabase() ) + { + EmitError( SPEW_SHAREDOBJ, "Unable to add singleton type %d for %s\n", pSchObj->GetTypeID(), GetOwner().Render() ); + return false; + } + AddObjectClean( pSchObj ); + return true; + } +} + + +//---------------------------------------------------------------------------- +// Purpose: Loads a list of CProtoBufSharedObjects from a result list from a +// query. +// Inputs: pResultSet - The result set from the SQL query +// schDefaults - A schema object that defines the values to set in +// the new objects for fields that were not read in the query. +// Typically this will be whatever fields were in the WHERE +// clause of the query. +// csRead - A columnSet defining the fields that were read in the query. +//---------------------------------------------------------------------------- +template< typename SOClass_t, typename SchClass_t > +bool CGCSharedObjectCache::BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead ) +{ + if ( NULL == pResultSet ) + return false; + + //don't bother creating a cache if we don't have objects to add into it + if( pResultSet->GetRowCount() > 0 ) + { + CGCSharedObjectTypeCache *pTypeCache = CreateTypeCache( SOClass_t::k_nTypeID ); + pTypeCache->EnsureCapacity( pResultSet->GetRowCount() ); + for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() ) + { + SchClass_t schRecord; + record.BWriteToRecord( &schRecord, csRead ); + + SOClass_t *pObj = new SOClass_t(); + pObj->ReadFromRecord( schRecord ); + pTypeCache->AddObjectClean( pObj ); + } + } + + return true; +} + + +//---------------------------------------------------------------------------- +// Purpose: Loads a single object of a type. If the object is not available, +// a new object will be created at default values +// Inputs: pResultSet - The result set from the SQL query +// schDefaults - A schema object that defines the values to set in +// the new objects for fields that were not read in the query. +// Typically this will be whatever fields were in the WHERE +// clause of the query. +// csRead - A columnSet defining the fields that were read in the query. +//---------------------------------------------------------------------------- +template< typename SOClass_t, typename SchClass_t > +bool CGCSharedObjectCache::BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults ) +{ + if ( NULL == pResultSet ) + return false; + + if ( pResultSet->GetRowCount() > 1 ) + { + EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadProtoBufSingleton() on type %d\n", SOClass_t::k_nTypeID ); + return false; + } + + // load the duel summary + SchClass_t schRead; + CSQLRecord record( 0, pResultSet ); + if( record.IsValid() ) + { + record.BWriteToRecord( &schRead, csRead ); + } + else + { + CSQLAccess sqlAccess; + if( !sqlAccess.BYieldingInsertRecord( const_cast<SchClass_t *>( &schDefaults ) ) ) + return false; + schRead = schDefaults; + } + + SOClass_t *pSharedObject = new SOClass_t(); + pSharedObject->ReadFromRecord( schRead ); + AddObjectClean( pSharedObject ); + + return true; +} + + +} // namespace GCSDK + +#include "tier0/memdbgoff.h" + +#endif //GC_SHAREDOBJECTCACHE_H |