Team Fortress 2 Source Code as on 22/4/2020
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.

409 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Additional shared object cache functionality for the GC
  4. //
  5. //=============================================================================
  6. #ifndef GC_SHAREDOBJECTCACHE_H
  7. #define GC_SHAREDOBJECTCACHE_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "sharedobjectcache.h"
  12. #include "tier1/utlhashtable.h"
  13. #include "gcdirtyfield.h"
  14. #include "gcsdk/gcsystemaccess.h"
  15. class CMsgSOCacheSubscribed_SubscribedType;
  16. #include "tier0/memdbgon.h"
  17. namespace GCSDK
  18. {
  19. class CCachedSubscriptionMessage;
  20. //----------------------------------------------------------------------------
  21. // Purpose: The part of a shared object cache that handles all objects of a
  22. // single type.
  23. //----------------------------------------------------------------------------
  24. class CSharedObjectContext
  25. {
  26. public:
  27. //holds information about a subscriber to the context
  28. struct Subscriber_t
  29. {
  30. CSteamID m_steamID; //the steam ID of the subscriber
  31. int m_nRefCount; //the number of references to this subscription
  32. };
  33. CSharedObjectContext( const CSteamID & steamIDOwner );
  34. bool BAddSubscriber( const CSteamID & steamID );
  35. bool BRemoveSubscriber( const CSteamID & steamID );
  36. void RemoveAllSubscribers();
  37. bool BIsSubscribed( const CSteamID & steamID ) const { return FindSubscriber( steamID ) != m_vecSubscribers.InvalidIndex(); }
  38. const CUtlVector< Subscriber_t > & GetSubscribers() const { return m_vecSubscribers; }
  39. const CSteamID & GetOwner() const { return m_steamIDOwner; }
  40. private:
  41. //finds a steam ID within the subscriber list, returns the index, invalid if it can't be found
  42. int FindSubscriber( const CSteamID& steamID ) const;
  43. CUtlVector<Subscriber_t> m_vecSubscribers;
  44. CSteamID m_steamIDOwner;
  45. };
  46. class CGCSharedObjectTypeCache;
  47. enum ESOTypeFlags
  48. {
  49. k_ESOFlag_SendToNobody = 0,
  50. k_ESOFlag_SendToOwner = 1 << 0, // will go to the owner of the cache (user or gameserver), if he is subscribed
  51. k_ESOFlag_SendToOtherUsers = 1 << 1, // will go to subscribed users who are not the owner of the cache
  52. k_ESOFlag_SendToOtherGameservers = 1 << 2, // will go to subscribed gameservers who are not the owner of the cache
  53. k_ESOFlag_SendToQuestObjectiveTrackers = 1 << 3,
  54. k_ESOFlag_LastFlag = k_ESOFlag_SendToQuestObjectiveTrackers,
  55. };
  56. //----------------------------------------------------------------------------
  57. // Purpose: Filter object used to determine whether a type cache's objects should
  58. // should be sent to subscribers and whether each object should be sent
  59. //----------------------------------------------------------------------------
  60. class CISubscriberMessageFilter
  61. {
  62. public:
  63. virtual bool BShouldSendAnyObjectsInCache( CGCSharedObjectTypeCache *pTypeCache, uint32 unFlags ) const = 0;
  64. virtual bool BShouldSendObject( CSharedObject *pSharedObject, uint32 unFlags ) const = 0;
  65. };
  66. //----------------------------------------------------------------------------
  67. // Purpose: The part of a shared object cache that handles all objects of a
  68. // single type.
  69. //----------------------------------------------------------------------------
  70. class CGCSharedObjectTypeCache : public CSharedObjectTypeCache
  71. {
  72. public:
  73. typedef CSharedObjectTypeCache Base;
  74. CGCSharedObjectTypeCache( int nTypeID, const CSharedObjectContext & context );
  75. virtual ~CGCSharedObjectTypeCache();
  76. virtual bool AddObject( CSharedObject *pObject );
  77. virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
  78. inline CSteamID GetOwner() const { return m_context.GetOwner(); }
  79. void BuildCacheSubscribedMsg( CMsgSOCacheSubscribed_SubscribedType *pMsgType, uint32 unFlags, const CISubscriberMessageFilter &filter );
  80. virtual void EnsureCapacity( uint32 nItems );
  81. #ifdef DBGFLAG_VALIDATE
  82. virtual void Validate( CValidator &validator, const char *pchName );
  83. #endif
  84. private:
  85. const CSharedObjectContext & m_context;
  86. };
  87. //----------------------------------------------------------------------------
  88. // Purpose: A cache of a bunch of shared objects of different types. This class
  89. // is shared between clients, gameservers, and the GC and is
  90. // responsible for sending messages from the GC to cause object
  91. // creation/destruction/updating on the clients/gameservers.
  92. //----------------------------------------------------------------------------
  93. class CGCSharedObjectCache : public CSharedObjectCache
  94. {
  95. public:
  96. CGCSharedObjectCache( const CSteamID & steamIDOwner = CSteamID() );
  97. virtual ~CGCSharedObjectCache();
  98. const CSteamID & GetOwner() const { return m_context.GetOwner(); }
  99. const CUtlVector< CSharedObjectContext::Subscriber_t > & GetSubscribers() const { return m_context.GetSubscribers(); }
  100. CGCSharedObjectTypeCache *FindTypeCache( int nClassID ) const { return (CGCSharedObjectTypeCache *)FindBaseTypeCache( nClassID ); }
  101. CGCSharedObjectTypeCache *CreateTypeCache( int nClassID ) { return (CGCSharedObjectTypeCache *)CreateBaseTypeCache( nClassID ); }
  102. virtual uint32 CalcSendFlags( const CSteamID &steamID ) const;
  103. virtual const CISubscriberMessageFilter &GetSubscriberMessageFilter();
  104. virtual bool AddObject( CSharedObject *pSharedObject );
  105. virtual bool AddObjectClean( CSharedObject *pSharedObject );
  106. bool BDestroyObject( const CSharedObject & soIndex, bool bRemoveFromDatabase );
  107. virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
  108. template< typename SOClass_t >
  109. bool BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults );
  110. template< typename SOClass_t >
  111. bool BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults );
  112. template< typename SOClass_t, typename SchClass_t >
  113. bool BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead );
  114. template< typename SOClass_t, typename SchClass_t >
  115. bool BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults );
  116. // @todo temporary for trading and item subscriptions (to be removed once we get cross-game trading)
  117. virtual void SetTradingPartner( const CSteamID &steamID );
  118. const CSteamID &GetTradingPartner() const { return m_steamIDTradingPartner; }
  119. void AddSubscriber( const CSteamID & steamID, bool bForceSendSubscriptionMsg = false );
  120. void RemoveSubscriber( const CSteamID & steamID );
  121. void RemoveAllSubscribers();
  122. void SendSubscriberMessage( const CSteamID & steamID );
  123. bool BIsSubscribed( const CSteamID & steamID ) { return m_context.BIsSubscribed( steamID ); }
  124. void ClearCachedSubscriptionMessage();
  125. bool BIsDatabaseDirty() const { return m_databaseDirtyList.NumDirtyObjects() > 0; }
  126. // This will mark the field as dirty for both network and database
  127. void DirtyObjectField( CSharedObject *pObj, int nFieldIndex );
  128. // Marks only dirty for network
  129. void DirtyNetworkObject( CSharedObject *pObj );
  130. void DirtyNetworkObjectCreate( CSharedObject *pObj );
  131. // Mark dirty for database
  132. void DirtyDatabaseObjectField( CSharedObject *pObj, int nFieldIndex );
  133. void SendNetworkUpdates( CSharedObject *pObj );
  134. // Add a specific object write to a transaction. The cache is expected to remain locked until this transaction is
  135. // closed. If the transaction is rolled back, the object will be returned to the dirty list.
  136. bool BYieldingAddWriteToTransaction( CSharedObject *pObj, CSQLAccess & sqlAccess );
  137. // Add all pending object writes to a transaction. The cache is expected to remain locked until this transaction is
  138. // closed. If the transaction successfully commits, the dirty list will be flushed.
  139. //
  140. // This is intended for use in writeback -- no changes to the dirty objects list may occur until this transaction
  141. // closes.
  142. uint32 YieldingStageAllWrites( CSQLAccess & sqlAccess );
  143. void SendAllNetworkUpdates();
  144. void FlushInventoryCache();
  145. void YieldingWriteToDatabase( CSharedObject *pObj );
  146. void SetInWriteback( bool bInWriteback );
  147. bool GetInWriteback() const { return m_bInWriteback; }
  148. RTime32 GetWritebackTime() const { return m_unWritebackTime; }
  149. void SetLRUHandle( uint32 unLRUHandle ) { m_unLRUHandle = unLRUHandle; }
  150. uint32 GetLRUHandle() const { return m_unLRUHandle; }
  151. void Dump() const;
  152. void DumpDirtyObjects() const;
  153. #ifdef DBGFLAG_VALIDATE
  154. virtual void Validate( CValidator &validator, const char *pchName );
  155. #endif
  156. bool IsObjectCached( const CSharedObject *pObj ) const { return IsObjectCached( pObj, pObj->GetTypeID() ); }
  157. //the same as the above, but takes in the type since if called from a destructor, you can't use virtual functions
  158. bool IsObjectCached( const CSharedObject *pObj, uint32 nTypeID ) const;
  159. bool IsObjectDirty( const CSharedObject *pObj ) const;
  160. //called to mark that we are no longer loading
  161. void SetDetectVersionChanges( bool bState ) { m_bDetectVersionChanges = bState; }
  162. protected:
  163. virtual CSharedObjectTypeCache *AllocateTypeCache( int nClassID ) const OVERRIDE { return new CGCSharedObjectTypeCache( nClassID, m_context ); }
  164. virtual void MarkDirty();
  165. virtual bool BShouldSendToAnyClients( uint32 unFlags ) const;
  166. CCachedSubscriptionMessage *BuildSubscriberMessage( uint32 unFlags );
  167. CSteamID m_steamIDTradingPartner;
  168. protected:
  169. void SendNetworkCreateInternal( CSharedObject * pObj );
  170. void SendNetworkUpdateInternal( CSharedObject * pObj );
  171. void SendUnsubscribeMessage( const CSteamID & steamID );
  172. //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
  173. //can cause inefficiencies
  174. bool m_bDetectVersionChanges;
  175. CSharedObjectContext m_context;
  176. CUtlHashtable< CSharedObject * > m_networkDirtyObjs;
  177. CUtlHashtable< CSharedObject * > m_networkDirtyObjsCreate;
  178. CSharedObjectDirtyList m_databaseDirtyList;
  179. bool m_bInWriteback;
  180. RTime32 m_unWritebackTime;
  181. uint32 m_unLRUHandle;
  182. uint32 m_unCachedSubscriptionMsgFlags;
  183. CCachedSubscriptionMessage *m_pCachedSubscriptionMsg;
  184. };
  185. //----------------------------------------------------------------------------
  186. // Purpose: Loads a list of CSchemaSharedObjects from a result list from a
  187. // query.
  188. // Inputs: pResultSet - The result set from the SQL query
  189. // schDefaults - A schema object that defines the values to set in
  190. // the new objects for fields that were not read in the query.
  191. // Typically this will be whatever fields were in the WHERE
  192. // clause of the query.
  193. // csRead - A columnSet defining the fields that were read in the query.
  194. //----------------------------------------------------------------------------
  195. template< typename SOClass_t >
  196. bool CGCSharedObjectCache::BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults )
  197. {
  198. if ( NULL == pResultSet )
  199. return false;
  200. //don't bother creating a cache if we don't have objects to add into it
  201. if( pResultSet->GetRowCount() > 0 )
  202. {
  203. CGCSharedObjectTypeCache *pTypeCache = CreateTypeCache( SOClass_t::k_nTypeID );
  204. pTypeCache->EnsureCapacity( pResultSet->GetRowCount() );
  205. for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() )
  206. {
  207. SOClass_t *pObj = new SOClass_t();
  208. pObj->Obj() = objDefaults.Obj();
  209. record.BWriteToRecord( &pObj->Obj(), csRead );
  210. pTypeCache->AddObjectClean( pObj );
  211. }
  212. }
  213. return true;
  214. }
  215. //----------------------------------------------------------------------------
  216. // Purpose: Loads a single object of a type. If the object is not available,
  217. // a new object will be created at default values
  218. // Inputs: pResultSet - The result set from the SQL query
  219. // schDefaults - A schema object that defines the values to set in
  220. // the new objects for fields that were not read in the query.
  221. // Typically this will be whatever fields were in the WHERE
  222. // clause of the query.
  223. // csRead - A columnSet defining the fields that were read in the query.
  224. //----------------------------------------------------------------------------
  225. template< typename SOClass_t >
  226. bool CGCSharedObjectCache::BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults )
  227. {
  228. if ( NULL == pResultSet )
  229. return false;
  230. if ( pResultSet->GetRowCount() > 1 )
  231. {
  232. EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadSchSingleton() on type %d\n", objDefaults.GetTypeID() );
  233. return false;
  234. }
  235. else if ( pResultSet->GetRowCount() == 1 )
  236. {
  237. return BYieldingLoadSchObjects<SOClass_t>( pResultSet, csRead, objDefaults );
  238. }
  239. else
  240. {
  241. // Create it if there wasn't one
  242. SOClass_t *pSchObj = new SOClass_t();
  243. pSchObj->Obj() = objDefaults.Obj();
  244. if( !pSchObj->BYieldingAddToDatabase() )
  245. {
  246. EmitError( SPEW_SHAREDOBJ, "Unable to add singleton type %d for %s\n", pSchObj->GetTypeID(), GetOwner().Render() );
  247. return false;
  248. }
  249. AddObjectClean( pSchObj );
  250. return true;
  251. }
  252. }
  253. //----------------------------------------------------------------------------
  254. // Purpose: Loads a list of CProtoBufSharedObjects from a result list from a
  255. // query.
  256. // Inputs: pResultSet - The result set from the SQL query
  257. // schDefaults - A schema object that defines the values to set in
  258. // the new objects for fields that were not read in the query.
  259. // Typically this will be whatever fields were in the WHERE
  260. // clause of the query.
  261. // csRead - A columnSet defining the fields that were read in the query.
  262. //----------------------------------------------------------------------------
  263. template< typename SOClass_t, typename SchClass_t >
  264. bool CGCSharedObjectCache::BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead )
  265. {
  266. if ( NULL == pResultSet )
  267. return false;
  268. //don't bother creating a cache if we don't have objects to add into it
  269. if( pResultSet->GetRowCount() > 0 )
  270. {
  271. CGCSharedObjectTypeCache *pTypeCache = CreateTypeCache( SOClass_t::k_nTypeID );
  272. pTypeCache->EnsureCapacity( pResultSet->GetRowCount() );
  273. for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() )
  274. {
  275. SchClass_t schRecord;
  276. record.BWriteToRecord( &schRecord, csRead );
  277. SOClass_t *pObj = new SOClass_t();
  278. pObj->ReadFromRecord( schRecord );
  279. pTypeCache->AddObjectClean( pObj );
  280. }
  281. }
  282. return true;
  283. }
  284. //----------------------------------------------------------------------------
  285. // Purpose: Loads a single object of a type. If the object is not available,
  286. // a new object will be created at default values
  287. // Inputs: pResultSet - The result set from the SQL query
  288. // schDefaults - A schema object that defines the values to set in
  289. // the new objects for fields that were not read in the query.
  290. // Typically this will be whatever fields were in the WHERE
  291. // clause of the query.
  292. // csRead - A columnSet defining the fields that were read in the query.
  293. //----------------------------------------------------------------------------
  294. template< typename SOClass_t, typename SchClass_t >
  295. bool CGCSharedObjectCache::BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults )
  296. {
  297. if ( NULL == pResultSet )
  298. return false;
  299. if ( pResultSet->GetRowCount() > 1 )
  300. {
  301. EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadProtoBufSingleton() on type %d\n", SOClass_t::k_nTypeID );
  302. return false;
  303. }
  304. // load the duel summary
  305. SchClass_t schRead;
  306. CSQLRecord record( 0, pResultSet );
  307. if( record.IsValid() )
  308. {
  309. record.BWriteToRecord( &schRead, csRead );
  310. }
  311. else
  312. {
  313. CSQLAccess sqlAccess;
  314. if( !sqlAccess.BYieldingInsertRecord( const_cast<SchClass_t *>( &schDefaults ) ) )
  315. return false;
  316. schRead = schDefaults;
  317. }
  318. SOClass_t *pSharedObject = new SOClass_t();
  319. pSharedObject->ReadFromRecord( schRead );
  320. AddObjectClean( pSharedObject );
  321. return true;
  322. }
  323. } // namespace GCSDK
  324. #include "tier0/memdbgoff.h"
  325. #endif //GC_SHAREDOBJECTCACHE_H