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.

473 lines
17 KiB

  1. //====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #ifndef MSGPROTOBUF_H
  7. #define MSGPROTOBUF_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "msgbase.h"
  12. #include "gcmsg.h"
  13. #include "tier0/tslist.h"
  14. // eliminates a conflict with TYPE_BOOL in OSX
  15. #ifdef TYPE_BOOL
  16. #undef TYPE_BOOL
  17. #endif
  18. #pragma warning(push)
  19. #pragma warning( disable:4512 )
  20. #include <tier0/valve_minmax_off.h>
  21. #include "steammessages.pb.h"
  22. #include <tier0/valve_minmax_on.h>
  23. #pragma warning(pop)
  24. namespace GCSDK
  25. {
  26. //-----------------------------------------------------------------------------
  27. // CProtoBufNetPacket
  28. // Thin wrapper around raw CNetPacket which implements our IMsgNetPacket interface.
  29. //-----------------------------------------------------------------------------
  30. class CProtoBufNetPacket : public IMsgNetPacket
  31. {
  32. #ifdef GC
  33. DECLARE_CLASS_MEMPOOL( CProtoBufNetPacket );
  34. #endif
  35. public:
  36. CProtoBufNetPacket( CNetPacket *pNetPacket, GCProtoBufMsgSrc eReplyType, const CSteamID steamID, uint32 nGCDirIndex, MsgType_t msgType );
  37. //IMsgNetPacket
  38. virtual EMsgFormatType GetEMsgFormatType() const OVERRIDE { return k_EMsgFormatTypeProtocolBuffer; }
  39. virtual CNetPacket *GetCNetPacket() const OVERRIDE { return m_pNetPacket; }
  40. virtual uint8 *PubData() const OVERRIDE { return m_pNetPacket->PubData(); }
  41. virtual uint CubData() const OVERRIDE { return m_pNetPacket->CubData(); }
  42. virtual MsgType_t GetEMsg() const OVERRIDE { return m_msgType; }
  43. virtual JobID_t GetSourceJobID() const OVERRIDE { return m_pHeader->job_id_source(); }
  44. virtual JobID_t GetTargetJobID() const OVERRIDE { return m_pHeader->job_id_target(); }
  45. virtual void SetTargetJobID( JobID_t ulJobID ) OVERRIDE { m_pHeader->set_job_id_target( ulJobID ); }
  46. virtual CSteamID GetSteamID() const OVERRIDE { return m_steamID; }
  47. virtual void SetSteamID( CSteamID steamID ) OVERRIDE { m_steamID = steamID; }
  48. virtual AppId_t GetSourceAppID() const OVERRIDE { return m_pHeader->source_app_id(); };
  49. virtual void SetSourceAppID( AppId_t appId ) OVERRIDE { m_pHeader->set_source_app_id( appId ); }
  50. virtual bool BHasTargetJobName() const OVERRIDE { return m_pHeader->has_target_job_name(); }
  51. virtual const char *GetTargetJobName() const OVERRIDE { return m_pHeader->target_job_name().c_str(); }
  52. bool IsValid() const { return m_bIsValid; }
  53. ProtoBufMsgHeader_t &GetFixedHeader() const { return *( ( ProtoBufMsgHeader_t * )PubData() ); }
  54. CMsgProtoBufHeader *GetProtoHeader() const { return m_pHeader; }
  55. //called to obtain access to the body portion of the net packet associated with this message. Will return NULL if not in a valid state
  56. bool GetMsgBody( const uint8*& pubData, uint32& cubData ) const;
  57. private:
  58. virtual ~CProtoBufNetPacket();
  59. CNetPacket *m_pNetPacket;
  60. CMsgProtoBufHeader *m_pHeader;
  61. CSteamID m_steamID;
  62. MsgType_t m_msgType;
  63. bool m_bIsValid;
  64. };
  65. //-----------------------------------------------------------------------------
  66. // CProtoBufMsgBase - Base class for templated protobuf msgs. As much code is
  67. // in this class as possible to reduce template copy overhead
  68. //-----------------------------------------------------------------------------
  69. class CProtoBufMsgBase
  70. {
  71. public:
  72. // allows any kind of destination to be the target of an AsyncSend
  73. class IProtoBufSendHandler
  74. {
  75. public:
  76. virtual bool BAsyncSend( MsgType_t eMsg, const uint8 *pubMsgBytes, uint32 cubSize ) = 0;
  77. };
  78. // Receive constructor. We expect InitFromPacket will be called later
  79. CProtoBufMsgBase();
  80. // Send constructor. InitFromPacket must not be called later
  81. CProtoBufMsgBase( MsgType_t eMsgType );
  82. virtual ~CProtoBufMsgBase();
  83. bool InitFromPacket( IMsgNetPacket * pNetPacket );
  84. bool BAsyncSend( IProtoBufSendHandler & pSender ) const;
  85. bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler & pSender, const byte *pubBody, uint32 cubBody ) const;
  86. //free standing version to send a protobuff given a header and pre-serialized body. Primarily used for efficient message routing
  87. static bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const byte* pubBody, uint32 cubBody );
  88. //similar to the above, but sends a protobuf object that will be serialized into the buffer
  89. static bool BAsyncSendProto( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const ::google::protobuf::Message& proto );
  90. CMsgProtoBufHeader &Hdr() { return *m_pProtoBufHdr; }
  91. const CMsgProtoBufHeader &Hdr() const { return *m_pProtoBufHdr; }
  92. const CMsgProtoBufHeader &ConstHdr() const { return *m_pProtoBufHdr; }
  93. MsgType_t GetEMsg() const { return m_eMsg & (~k_EMsgProtoBufFlag); }
  94. CSteamID GetClientSteamID() const { return CSteamID( m_pProtoBufHdr->client_steam_id() ); }
  95. JobID_t GetJobIDTarget() const { return m_pProtoBufHdr->job_id_target(); }
  96. JobID_t GetJobIDSource() const { return m_pProtoBufHdr->job_id_source(); }
  97. AppId_t GetSourceAppID() const { return m_pProtoBufHdr->source_app_id(); }
  98. bool BIsExpectingReply() const { return GetJobIDSource() != k_GIDNil; }
  99. void SetJobIDSource( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_source( jobId ); }
  100. void SetJobIDTarget( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_target( jobId ); }
  101. void SetSourceAppID( AppId_t appId ) { m_pProtoBufHdr->set_source_app_id( appId ); }
  102. void ExpectingReply( JobID_t jobId ) { SetJobIDSource( jobId ); }
  103. EResult GetEResult() const { return (EResult)ConstHdr().eresult(); }
  104. void SetEResult( EResult eResult ) { Hdr().set_eresult( eResult ); }
  105. const char *GetErrorMessage() const { return ConstHdr().error_message().c_str(); }
  106. void SetErrorMessage( const char *pchErrorMessage ) { Hdr().set_error_message( pchErrorMessage ); }
  107. void AppendErrorMessage( const char *pchErrorMessage ) { Hdr().mutable_error_message()->append( pchErrorMessage ); }
  108. // Must be implemented by subclasses. Returns the body. The templated subclasses have their
  109. // own body accessor that returns the body as the specific type
  110. virtual ::google::protobuf::Message *GetGenericBody() const = 0;
  111. protected:
  112. // Mutex to use when registering a new pool type
  113. static CThreadMutex s_PoolRegMutex;
  114. private:
  115. //utility function that handles allocating a memory pool big enough for the provided header and specified body
  116. //size and writing the header into the pool. This will return a pointer to the memory, as well as the header size.
  117. static uint8* AllocateMessageMemory( MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, uint32 cubBodySize, uint32* pCubTotalSizeOut );
  118. //called to free the memory returned by allocate message memory
  119. static void FreeMessageMemory( uint8* pMemory );
  120. // Pointer to an external net packet if we have one. If we have one then we will
  121. // not have allocated m_pProtoBufHdr ourselves
  122. CProtoBufNetPacket *m_pNetPacket;
  123. // Protobuf objects for extended pb based header
  124. CMsgProtoBufHeader *m_pProtoBufHdr;
  125. // Our message type
  126. MsgType_t m_eMsg;
  127. // Private and unimplemented. Implement these if you want to be able to copy
  128. // these objects.
  129. CProtoBufMsgBase( const CProtoBufMsgBase& );
  130. CProtoBufMsgBase& operator=( const CProtoBufMsgBase& );
  131. };
  132. //-----------------------------------------------------------------------------
  133. // CProtoBufMsgMemoryPoolBase - Interface to allocation pools for each protobufmsg type
  134. //-----------------------------------------------------------------------------
  135. class CProtoBufMsgMemoryPoolBase
  136. {
  137. public:
  138. CProtoBufMsgMemoryPoolBase( uint32 unTargetLow, uint32 unTargetHigh );
  139. virtual ~CProtoBufMsgMemoryPoolBase();
  140. // Memory interface
  141. ::google::protobuf::Message *Alloc();
  142. void Free( ::google::protobuf::Message *pMsg );
  143. // Stats
  144. uint32 GetEstimatedSize();
  145. uint32 GetAllocated() { return m_unAllocated; }
  146. uint32 GetFree() { return m_pTSQueueFreeObjects->Count(); }
  147. uint32 GetAllocHitCount() { return m_unAllocHitCounter; }
  148. uint32 GetAllocMissCount() { return m_unAllocMissCounter; }
  149. // To be overriden by the templated class
  150. virtual CUtlString GetName() = 0;
  151. protected:
  152. // The actual memory management. Must be overriden by the templated class
  153. virtual google::protobuf::Message *InternalAlloc() = 0;
  154. virtual void InternalFree( google::protobuf::Message *pMsg ) = 0;
  155. // Called by the derived destructor to deallocate the outstanding messages
  156. bool PopItem( google::protobuf::Message **ppMsg );
  157. private:
  158. CTSQueue<google::protobuf::Message *> *m_pTSQueueFreeObjects;
  159. // These counters are important to get correct, so interlocked in case of allocating on threads
  160. CInterlockedInt m_unAllocHitCounter;
  161. CInterlockedInt m_unAllocMissCounter;
  162. CInterlockedInt m_unAllocated;
  163. // Only set at construction, so not needed to be thread safe
  164. uint32 m_unTargetCountLow;
  165. uint32 m_unTargetCountHigh;
  166. };
  167. } // namespace GCDSK
  168. // The rest of the file needs memdbgon because the code in the templates do actual allocation
  169. #include "tier0/memdbgon.h"
  170. namespace GCSDK
  171. {
  172. //-----------------------------------------------------------------------------
  173. // CProtoBufMsgMemoryPool - Implementation for allocation pools for protobufmsgs.
  174. // We create one of these per protobuf msg type, created on first construction of
  175. // an object of that type.
  176. //-----------------------------------------------------------------------------
  177. template< typename PB_OBJECT_TYPE >
  178. class CProtoBufMsgMemoryPool : public CProtoBufMsgMemoryPoolBase
  179. {
  180. public:
  181. CProtoBufMsgMemoryPool()
  182. : CProtoBufMsgMemoryPoolBase( PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_soft_limit ),
  183. PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_hard_limit ) ) {}
  184. virtual ~CProtoBufMsgMemoryPool()
  185. {
  186. google::protobuf::Message *pObject = NULL;
  187. while ( PopItem( &pObject ) )
  188. {
  189. InternalFree( pObject );
  190. }
  191. }
  192. virtual CUtlString GetName() OVERRIDE
  193. {
  194. return PB_OBJECT_TYPE::default_instance().GetTypeName().c_str();
  195. }
  196. private:
  197. virtual ::google::protobuf::Message *InternalAlloc()
  198. {
  199. PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)malloc( sizeof( PB_OBJECT_TYPE ) );
  200. Construct( pObject );
  201. return pObject;
  202. }
  203. virtual void InternalFree( google::protobuf::Message *pMsg )
  204. {
  205. if ( NULL == pMsg )
  206. {
  207. Assert( NULL != pMsg );
  208. return;
  209. }
  210. PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)pMsg;
  211. Destruct( pObject );
  212. FreePv( pObject );
  213. }
  214. };
  215. //-----------------------------------------------------------------------------
  216. // CProtoBufMsgMemoryPoolMgr - Manages all the message pools for protobufmsgs.
  217. // Should have one global singleton instance of this which tracks all the pools
  218. // for individual message types.
  219. //-----------------------------------------------------------------------------
  220. class CProtoBufMsgMemoryPoolMgr
  221. {
  222. public:
  223. CProtoBufMsgMemoryPoolMgr();
  224. ~CProtoBufMsgMemoryPoolMgr();
  225. void RegisterPool( CProtoBufMsgMemoryPoolBase *pPool );
  226. void DumpPoolInfo();
  227. CMsgProtoBufHeader *AllocProtoBufHdr() { return (CMsgProtoBufHeader *)m_PoolHeaders.Alloc(); }
  228. void FreeProtoBufHdr( CMsgProtoBufHeader *pObject ) { m_PoolHeaders.Free( pObject ); }
  229. private:
  230. CProtoBufMsgMemoryPool<CMsgProtoBufHeader> m_PoolHeaders;
  231. CUtlVector< CProtoBufMsgMemoryPoolBase * > m_vecMsgPools;
  232. };
  233. extern CProtoBufMsgMemoryPoolMgr *GProtoBufMsgMemoryPoolMgr();
  234. //-----------------------------------------------------------------------------
  235. // CProtoBufPtrMsg
  236. // Similar to a CProtoBufMsg, but the constructor simply takes in a pointer which is a
  237. // pointer to the protobuf object that is being wrapped by a message. This memory is managed
  238. // by the caller, this object does nothing to free the memory
  239. //-----------------------------------------------------------------------------
  240. class CProtoBufPtrMsg : public CProtoBufMsgBase
  241. {
  242. public:
  243. CProtoBufPtrMsg( google::protobuf::Message *pProto ) : m_pProtoBufBody( pProto ) {}
  244. private:
  245. virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
  246. // Protobuf object for the message body
  247. google::protobuf::Message *m_pProtoBufBody;
  248. // Private and unimplemented. Implement these if you want to be able to copy
  249. // these objects.
  250. CProtoBufPtrMsg( const CProtoBufPtrMsg& );
  251. CProtoBufPtrMsg& operator=( const CProtoBufPtrMsg& );
  252. };
  253. //-----------------------------------------------------------------------------
  254. // CProtoBufMsg
  255. // New style steam inter-server message class based on Google Protocol Buffers
  256. // Handles a message with a header of type MsgHdr_t, a body of type T, and optional variable length data
  257. //-----------------------------------------------------------------------------
  258. template< typename PB_OBJECT_TYPE >
  259. class CProtoBufMsg : public CProtoBufMsgBase
  260. {
  261. private:
  262. static bool s_bRegisteredWithMemoryPoolMgr;
  263. static CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *s_pMemoryPool;
  264. public:
  265. // Used to alloc a protobuf of this type from the pool. Can be used by functions
  266. // working with protobufs that aren't messages to take advantage of pooling
  267. static PB_OBJECT_TYPE *AllocProto()
  268. {
  269. // If we haven't done registration do so now
  270. // Called on construction of each object of this type, but only does work
  271. // once to setup memory pools for the class type.
  272. if ( !s_bRegisteredWithMemoryPoolMgr )
  273. {
  274. // Get the lock and make sure we still haven't
  275. s_PoolRegMutex.Lock();
  276. if ( !s_bRegisteredWithMemoryPoolMgr )
  277. {
  278. s_pMemoryPool = new CProtoBufMsgMemoryPool< PB_OBJECT_TYPE >();
  279. GProtoBufMsgMemoryPoolMgr()->RegisterPool( s_pMemoryPool );
  280. s_bRegisteredWithMemoryPoolMgr = true;
  281. }
  282. s_PoolRegMutex.Unlock();
  283. }
  284. return static_cast<PB_OBJECT_TYPE *>( s_pMemoryPool->Alloc() );
  285. }
  286. // Frees a protobuf allocated with AllocProto()
  287. static void FreeProto( PB_OBJECT_TYPE *pbObj )
  288. {
  289. s_pMemoryPool->Free( pbObj );
  290. }
  291. // Constructor for an empty message
  292. CProtoBufMsg( MsgType_t eMsg )
  293. : CProtoBufMsgBase( eMsg )
  294. , m_pProtoBufBody( NULL )
  295. {
  296. VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  297. m_pProtoBufBody = AllocProto();
  298. }
  299. // Constructor for an empty message responding to a client
  300. CProtoBufMsg( MsgType_t eMsg, CSteamID steamIDClient, int32 nSessionIDClient )
  301. : CProtoBufMsgBase( eMsg )
  302. , m_pProtoBufBody( NULL )
  303. {
  304. VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t, CSteamID, int32 )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  305. m_pProtoBufBody = AllocProto();
  306. Hdr()->set_client_steam_id( steamIDClient.ConvertToUint64() );
  307. Hdr()->set_client_session_id( nSessionIDClient );
  308. }
  309. // Constructor from an incoming netpacket
  310. CProtoBufMsg( IMsgNetPacket *pNetPacket )
  311. : CProtoBufMsgBase()
  312. , m_pProtoBufBody( NULL )
  313. {
  314. m_pProtoBufBody = AllocProto();
  315. InitFromPacket( pNetPacket );
  316. }
  317. // constructor for use in catching replies or any other place where you have nothing to stuff in
  318. // the message at construct time
  319. CProtoBufMsg()
  320. : CProtoBufMsgBase()
  321. , m_pProtoBufBody( NULL )
  322. {
  323. m_pProtoBufBody = AllocProto();
  324. }
  325. // Constructor for replying to another protobuf message
  326. CProtoBufMsg( MsgType_t eMsg, const CProtoBufMsgBase & msgReplyingTo )
  327. : CProtoBufMsgBase( eMsg )
  328. , m_pProtoBufBody( NULL )
  329. {
  330. VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( EMsg, CProtoBufMsgMemoryPoolBase )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  331. m_pProtoBufBody = AllocProto();
  332. // set up the actual reply
  333. SetJobIDTarget( msgReplyingTo.GetJobIDSource() );
  334. }
  335. // Destructor
  336. virtual ~CProtoBufMsg()
  337. {
  338. if ( m_pProtoBufBody )
  339. {
  340. FreeProto( m_pProtoBufBody );
  341. m_pProtoBufBody = NULL;
  342. }
  343. }
  344. // Accessors
  345. PB_OBJECT_TYPE &Body() { return *m_pProtoBufBody; }
  346. const PB_OBJECT_TYPE &Body() const { return *m_pProtoBufBody; }
  347. private:
  348. virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
  349. // Protobuf object for the message body
  350. PB_OBJECT_TYPE *m_pProtoBufBody;
  351. // Private and unimplemented. Implement these if you want to be able to copy
  352. // these objects.
  353. CProtoBufMsg( const CProtoBufMsg& );
  354. CProtoBufMsg& operator=( const CProtoBufMsg& );
  355. };
  356. // Statics
  357. template< typename PB_OBJECT_TYPE > bool CProtoBufMsg< PB_OBJECT_TYPE>::s_bRegisteredWithMemoryPoolMgr = false;
  358. template< typename PB_OBJECT_TYPE > CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *CProtoBufMsg< PB_OBJECT_TYPE>::s_pMemoryPool = NULL;
  359. //-----------------------------------------------------------------------------
  360. // Purpose: Wrapper class to handle alloc/free using the pool allocators
  361. //-----------------------------------------------------------------------------
  362. template <class TMsg>
  363. class CProtoBufPoolObj
  364. {
  365. private:
  366. TMsg *m_pMsg;
  367. private: // Disallow copying/assignment
  368. CProtoBufPoolObj( CProtoBufPoolObj const &x );
  369. CProtoBufPoolObj & operator = ( CProtoBufPoolObj const &x );
  370. public:
  371. CProtoBufPoolObj() { m_pMsg = CProtoBufMsg<TMsg>::AllocProto(); }
  372. ~CProtoBufPoolObj() { CProtoBufMsg<TMsg>::FreeProto( m_pMsg ); }
  373. operator TMsg & () { return *m_pMsg; }
  374. };
  375. } // namespace GCSDK
  376. // memdbgon is only supposed to be on in cpp files, turn it off in case the next thing includes has a conflict with it
  377. #include "tier0/memdbgoff.h"
  378. #endif // MSGPROTOBUF_H