summaryrefslogtreecommitdiff
path: root/public/gcsdk/msgprotobuf.h
diff options
context:
space:
mode:
Diffstat (limited to 'public/gcsdk/msgprotobuf.h')
-rw-r--r--public/gcsdk/msgprotobuf.h473
1 files changed, 473 insertions, 0 deletions
diff --git a/public/gcsdk/msgprotobuf.h b/public/gcsdk/msgprotobuf.h
new file mode 100644
index 0000000..0d51003
--- /dev/null
+++ b/public/gcsdk/msgprotobuf.h
@@ -0,0 +1,473 @@
+//====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef MSGPROTOBUF_H
+#define MSGPROTOBUF_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "msgbase.h"
+#include "gcmsg.h"
+#include "tier0/tslist.h"
+
+// eliminates a conflict with TYPE_BOOL in OSX
+#ifdef TYPE_BOOL
+#undef TYPE_BOOL
+#endif
+
+#pragma warning(push)
+#pragma warning( disable:4512 )
+#include <tier0/valve_minmax_off.h>
+#include "steammessages.pb.h"
+#include <tier0/valve_minmax_on.h>
+#pragma warning(pop)
+
+
+namespace GCSDK
+{
+
+//-----------------------------------------------------------------------------
+// CProtoBufNetPacket
+// Thin wrapper around raw CNetPacket which implements our IMsgNetPacket interface.
+//-----------------------------------------------------------------------------
+class CProtoBufNetPacket : public IMsgNetPacket
+{
+#ifdef GC
+ DECLARE_CLASS_MEMPOOL( CProtoBufNetPacket );
+#endif
+
+public:
+ CProtoBufNetPacket( CNetPacket *pNetPacket, GCProtoBufMsgSrc eReplyType, const CSteamID steamID, uint32 nGCDirIndex, MsgType_t msgType );
+
+ //IMsgNetPacket
+
+ virtual EMsgFormatType GetEMsgFormatType() const OVERRIDE { return k_EMsgFormatTypeProtocolBuffer; }
+ virtual CNetPacket *GetCNetPacket() const OVERRIDE { return m_pNetPacket; }
+ virtual uint8 *PubData() const OVERRIDE { return m_pNetPacket->PubData(); }
+ virtual uint CubData() const OVERRIDE { return m_pNetPacket->CubData(); }
+
+ virtual MsgType_t GetEMsg() const OVERRIDE { return m_msgType; }
+ virtual JobID_t GetSourceJobID() const OVERRIDE { return m_pHeader->job_id_source(); }
+ virtual JobID_t GetTargetJobID() const OVERRIDE { return m_pHeader->job_id_target(); }
+ virtual void SetTargetJobID( JobID_t ulJobID ) OVERRIDE { m_pHeader->set_job_id_target( ulJobID ); }
+
+ virtual CSteamID GetSteamID() const OVERRIDE { return m_steamID; }
+ virtual void SetSteamID( CSteamID steamID ) OVERRIDE { m_steamID = steamID; }
+
+ virtual AppId_t GetSourceAppID() const OVERRIDE { return m_pHeader->source_app_id(); };
+ virtual void SetSourceAppID( AppId_t appId ) OVERRIDE { m_pHeader->set_source_app_id( appId ); }
+
+ virtual bool BHasTargetJobName() const OVERRIDE { return m_pHeader->has_target_job_name(); }
+ virtual const char *GetTargetJobName() const OVERRIDE { return m_pHeader->target_job_name().c_str(); }
+
+
+ bool IsValid() const { return m_bIsValid; }
+ ProtoBufMsgHeader_t &GetFixedHeader() const { return *( ( ProtoBufMsgHeader_t * )PubData() ); }
+ CMsgProtoBufHeader *GetProtoHeader() const { return m_pHeader; }
+
+ //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
+ bool GetMsgBody( const uint8*& pubData, uint32& cubData ) const;
+
+private:
+ virtual ~CProtoBufNetPacket();
+
+ CNetPacket *m_pNetPacket;
+ CMsgProtoBufHeader *m_pHeader;
+ CSteamID m_steamID;
+ MsgType_t m_msgType;
+ bool m_bIsValid;
+};
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgBase - Base class for templated protobuf msgs. As much code is
+// in this class as possible to reduce template copy overhead
+//-----------------------------------------------------------------------------
+class CProtoBufMsgBase
+{
+public:
+ // allows any kind of destination to be the target of an AsyncSend
+ class IProtoBufSendHandler
+ {
+ public:
+ virtual bool BAsyncSend( MsgType_t eMsg, const uint8 *pubMsgBytes, uint32 cubSize ) = 0;
+ };
+
+ // Receive constructor. We expect InitFromPacket will be called later
+ CProtoBufMsgBase();
+ // Send constructor. InitFromPacket must not be called later
+ CProtoBufMsgBase( MsgType_t eMsgType );
+
+ virtual ~CProtoBufMsgBase();
+
+ bool InitFromPacket( IMsgNetPacket * pNetPacket );
+ bool BAsyncSend( IProtoBufSendHandler & pSender ) const;
+ bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler & pSender, const byte *pubBody, uint32 cubBody ) const;
+ //free standing version to send a protobuff given a header and pre-serialized body. Primarily used for efficient message routing
+ static bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const byte* pubBody, uint32 cubBody );
+ //similar to the above, but sends a protobuf object that will be serialized into the buffer
+ static bool BAsyncSendProto( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const ::google::protobuf::Message& proto );
+
+ CMsgProtoBufHeader &Hdr() { return *m_pProtoBufHdr; }
+ const CMsgProtoBufHeader &Hdr() const { return *m_pProtoBufHdr; }
+ const CMsgProtoBufHeader &ConstHdr() const { return *m_pProtoBufHdr; }
+
+ MsgType_t GetEMsg() const { return m_eMsg & (~k_EMsgProtoBufFlag); }
+ CSteamID GetClientSteamID() const { return CSteamID( m_pProtoBufHdr->client_steam_id() ); }
+ JobID_t GetJobIDTarget() const { return m_pProtoBufHdr->job_id_target(); }
+ JobID_t GetJobIDSource() const { return m_pProtoBufHdr->job_id_source(); }
+ AppId_t GetSourceAppID() const { return m_pProtoBufHdr->source_app_id(); }
+ bool BIsExpectingReply() const { return GetJobIDSource() != k_GIDNil; }
+
+ void SetJobIDSource( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_source( jobId ); }
+ void SetJobIDTarget( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_target( jobId ); }
+ void SetSourceAppID( AppId_t appId ) { m_pProtoBufHdr->set_source_app_id( appId ); }
+ void ExpectingReply( JobID_t jobId ) { SetJobIDSource( jobId ); }
+
+ EResult GetEResult() const { return (EResult)ConstHdr().eresult(); }
+ void SetEResult( EResult eResult ) { Hdr().set_eresult( eResult ); }
+ const char *GetErrorMessage() const { return ConstHdr().error_message().c_str(); }
+ void SetErrorMessage( const char *pchErrorMessage ) { Hdr().set_error_message( pchErrorMessage ); }
+ void AppendErrorMessage( const char *pchErrorMessage ) { Hdr().mutable_error_message()->append( pchErrorMessage ); }
+
+ // Must be implemented by subclasses. Returns the body. The templated subclasses have their
+ // own body accessor that returns the body as the specific type
+ virtual ::google::protobuf::Message *GetGenericBody() const = 0;
+
+protected:
+
+ // Mutex to use when registering a new pool type
+ static CThreadMutex s_PoolRegMutex;
+
+private:
+
+ //utility function that handles allocating a memory pool big enough for the provided header and specified body
+ //size and writing the header into the pool. This will return a pointer to the memory, as well as the header size.
+ static uint8* AllocateMessageMemory( MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, uint32 cubBodySize, uint32* pCubTotalSizeOut );
+ //called to free the memory returned by allocate message memory
+ static void FreeMessageMemory( uint8* pMemory );
+
+ // Pointer to an external net packet if we have one. If we have one then we will
+ // not have allocated m_pProtoBufHdr ourselves
+ CProtoBufNetPacket *m_pNetPacket;
+
+ // Protobuf objects for extended pb based header
+ CMsgProtoBufHeader *m_pProtoBufHdr;
+
+ // Our message type
+ MsgType_t m_eMsg;
+
+ // Private and unimplemented. Implement these if you want to be able to copy
+ // these objects.
+ CProtoBufMsgBase( const CProtoBufMsgBase& );
+ CProtoBufMsgBase& operator=( const CProtoBufMsgBase& );
+};
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgMemoryPoolBase - Interface to allocation pools for each protobufmsg type
+//-----------------------------------------------------------------------------
+class CProtoBufMsgMemoryPoolBase
+{
+public:
+ CProtoBufMsgMemoryPoolBase( uint32 unTargetLow, uint32 unTargetHigh );
+ virtual ~CProtoBufMsgMemoryPoolBase();
+
+ // Memory interface
+ ::google::protobuf::Message *Alloc();
+ void Free( ::google::protobuf::Message *pMsg );
+
+ // Stats
+ uint32 GetEstimatedSize();
+ uint32 GetAllocated() { return m_unAllocated; }
+ uint32 GetFree() { return m_pTSQueueFreeObjects->Count(); }
+ uint32 GetAllocHitCount() { return m_unAllocHitCounter; }
+ uint32 GetAllocMissCount() { return m_unAllocMissCounter; }
+
+ // To be overriden by the templated class
+ virtual CUtlString GetName() = 0;
+
+protected:
+ // The actual memory management. Must be overriden by the templated class
+ virtual google::protobuf::Message *InternalAlloc() = 0;
+ virtual void InternalFree( google::protobuf::Message *pMsg ) = 0;
+
+ // Called by the derived destructor to deallocate the outstanding messages
+ bool PopItem( google::protobuf::Message **ppMsg );
+
+private:
+ CTSQueue<google::protobuf::Message *> *m_pTSQueueFreeObjects;
+
+ // These counters are important to get correct, so interlocked in case of allocating on threads
+ CInterlockedInt m_unAllocHitCounter;
+ CInterlockedInt m_unAllocMissCounter;
+ CInterlockedInt m_unAllocated;
+
+ // Only set at construction, so not needed to be thread safe
+ uint32 m_unTargetCountLow;
+ uint32 m_unTargetCountHigh;
+};
+
+} // namespace GCDSK
+
+// The rest of the file needs memdbgon because the code in the templates do actual allocation
+#include "tier0/memdbgon.h"
+
+namespace GCSDK
+{
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgMemoryPool - Implementation for allocation pools for protobufmsgs.
+// We create one of these per protobuf msg type, created on first construction of
+// an object of that type.
+//-----------------------------------------------------------------------------
+template< typename PB_OBJECT_TYPE >
+class CProtoBufMsgMemoryPool : public CProtoBufMsgMemoryPoolBase
+{
+public:
+ CProtoBufMsgMemoryPool()
+ : CProtoBufMsgMemoryPoolBase( PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_soft_limit ),
+ PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_hard_limit ) ) {}
+ virtual ~CProtoBufMsgMemoryPool()
+ {
+ google::protobuf::Message *pObject = NULL;
+ while ( PopItem( &pObject ) )
+ {
+ InternalFree( pObject );
+ }
+ }
+
+ virtual CUtlString GetName() OVERRIDE
+ {
+ return PB_OBJECT_TYPE::default_instance().GetTypeName().c_str();
+ }
+
+private:
+ virtual ::google::protobuf::Message *InternalAlloc()
+ {
+ PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)malloc( sizeof( PB_OBJECT_TYPE ) );
+ Construct( pObject );
+ return pObject;
+ }
+
+ virtual void InternalFree( google::protobuf::Message *pMsg )
+ {
+ if ( NULL == pMsg )
+ {
+ Assert( NULL != pMsg );
+ return;
+ }
+
+ PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)pMsg;
+ Destruct( pObject );
+ FreePv( pObject );
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsgMemoryPoolMgr - Manages all the message pools for protobufmsgs.
+// Should have one global singleton instance of this which tracks all the pools
+// for individual message types.
+//-----------------------------------------------------------------------------
+class CProtoBufMsgMemoryPoolMgr
+{
+public:
+ CProtoBufMsgMemoryPoolMgr();
+ ~CProtoBufMsgMemoryPoolMgr();
+
+ void RegisterPool( CProtoBufMsgMemoryPoolBase *pPool );
+ void DumpPoolInfo();
+
+ CMsgProtoBufHeader *AllocProtoBufHdr() { return (CMsgProtoBufHeader *)m_PoolHeaders.Alloc(); }
+ void FreeProtoBufHdr( CMsgProtoBufHeader *pObject ) { m_PoolHeaders.Free( pObject ); }
+
+private:
+ CProtoBufMsgMemoryPool<CMsgProtoBufHeader> m_PoolHeaders;
+ CUtlVector< CProtoBufMsgMemoryPoolBase * > m_vecMsgPools;
+};
+
+extern CProtoBufMsgMemoryPoolMgr *GProtoBufMsgMemoryPoolMgr();
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufPtrMsg
+// Similar to a CProtoBufMsg, but the constructor simply takes in a pointer which is a
+// pointer to the protobuf object that is being wrapped by a message. This memory is managed
+// by the caller, this object does nothing to free the memory
+//-----------------------------------------------------------------------------
+class CProtoBufPtrMsg : public CProtoBufMsgBase
+{
+public:
+ CProtoBufPtrMsg( google::protobuf::Message *pProto ) : m_pProtoBufBody( pProto ) {}
+
+private:
+ virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
+
+ // Protobuf object for the message body
+ google::protobuf::Message *m_pProtoBufBody;
+
+ // Private and unimplemented. Implement these if you want to be able to copy
+ // these objects.
+ CProtoBufPtrMsg( const CProtoBufPtrMsg& );
+ CProtoBufPtrMsg& operator=( const CProtoBufPtrMsg& );
+};
+
+
+//-----------------------------------------------------------------------------
+// CProtoBufMsg
+// New style steam inter-server message class based on Google Protocol Buffers
+// Handles a message with a header of type MsgHdr_t, a body of type T, and optional variable length data
+//-----------------------------------------------------------------------------
+template< typename PB_OBJECT_TYPE >
+class CProtoBufMsg : public CProtoBufMsgBase
+{
+private:
+ static bool s_bRegisteredWithMemoryPoolMgr;
+ static CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *s_pMemoryPool;
+
+public:
+
+ // Used to alloc a protobuf of this type from the pool. Can be used by functions
+ // working with protobufs that aren't messages to take advantage of pooling
+ static PB_OBJECT_TYPE *AllocProto()
+ {
+ // If we haven't done registration do so now
+ // Called on construction of each object of this type, but only does work
+ // once to setup memory pools for the class type.
+ if ( !s_bRegisteredWithMemoryPoolMgr )
+ {
+ // Get the lock and make sure we still haven't
+ s_PoolRegMutex.Lock();
+ if ( !s_bRegisteredWithMemoryPoolMgr )
+ {
+ s_pMemoryPool = new CProtoBufMsgMemoryPool< PB_OBJECT_TYPE >();
+ GProtoBufMsgMemoryPoolMgr()->RegisterPool( s_pMemoryPool );
+ s_bRegisteredWithMemoryPoolMgr = true;
+ }
+ s_PoolRegMutex.Unlock();
+ }
+
+ return static_cast<PB_OBJECT_TYPE *>( s_pMemoryPool->Alloc() );
+ }
+
+ // Frees a protobuf allocated with AllocProto()
+ static void FreeProto( PB_OBJECT_TYPE *pbObj )
+ {
+ s_pMemoryPool->Free( pbObj );
+ }
+
+
+ // Constructor for an empty message
+ CProtoBufMsg( MsgType_t eMsg )
+ : CProtoBufMsgBase( eMsg )
+ , m_pProtoBufBody( NULL )
+ {
+ VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
+ m_pProtoBufBody = AllocProto();
+ }
+
+ // Constructor for an empty message responding to a client
+ CProtoBufMsg( MsgType_t eMsg, CSteamID steamIDClient, int32 nSessionIDClient )
+ : CProtoBufMsgBase( eMsg )
+ , m_pProtoBufBody( NULL )
+ {
+ VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t, CSteamID, int32 )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
+
+ m_pProtoBufBody = AllocProto();
+ Hdr()->set_client_steam_id( steamIDClient.ConvertToUint64() );
+ Hdr()->set_client_session_id( nSessionIDClient );
+ }
+
+ // Constructor from an incoming netpacket
+ CProtoBufMsg( IMsgNetPacket *pNetPacket )
+ : CProtoBufMsgBase()
+ , m_pProtoBufBody( NULL )
+ {
+ m_pProtoBufBody = AllocProto();
+ InitFromPacket( pNetPacket );
+ }
+
+ // constructor for use in catching replies or any other place where you have nothing to stuff in
+ // the message at construct time
+ CProtoBufMsg()
+ : CProtoBufMsgBase()
+ , m_pProtoBufBody( NULL )
+ {
+ m_pProtoBufBody = AllocProto();
+ }
+
+ // Constructor for replying to another protobuf message
+ CProtoBufMsg( MsgType_t eMsg, const CProtoBufMsgBase & msgReplyingTo )
+ : CProtoBufMsgBase( eMsg )
+ , m_pProtoBufBody( NULL )
+ {
+ VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( EMsg, CProtoBufMsgMemoryPoolBase )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
+ m_pProtoBufBody = AllocProto();
+
+ // set up the actual reply
+ SetJobIDTarget( msgReplyingTo.GetJobIDSource() );
+ }
+
+ // Destructor
+ virtual ~CProtoBufMsg()
+ {
+ if ( m_pProtoBufBody )
+ {
+ FreeProto( m_pProtoBufBody );
+ m_pProtoBufBody = NULL;
+ }
+ }
+
+ // Accessors
+ PB_OBJECT_TYPE &Body() { return *m_pProtoBufBody; }
+ const PB_OBJECT_TYPE &Body() const { return *m_pProtoBufBody; }
+
+private:
+ virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
+
+ // Protobuf object for the message body
+ PB_OBJECT_TYPE *m_pProtoBufBody;
+
+ // Private and unimplemented. Implement these if you want to be able to copy
+ // these objects.
+ CProtoBufMsg( const CProtoBufMsg& );
+ CProtoBufMsg& operator=( const CProtoBufMsg& );
+};
+
+// Statics
+template< typename PB_OBJECT_TYPE > bool CProtoBufMsg< PB_OBJECT_TYPE>::s_bRegisteredWithMemoryPoolMgr = false;
+template< typename PB_OBJECT_TYPE > CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *CProtoBufMsg< PB_OBJECT_TYPE>::s_pMemoryPool = NULL;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Wrapper class to handle alloc/free using the pool allocators
+//-----------------------------------------------------------------------------
+template <class TMsg>
+class CProtoBufPoolObj
+{
+private:
+ TMsg *m_pMsg;
+
+private: // Disallow copying/assignment
+ CProtoBufPoolObj( CProtoBufPoolObj const &x );
+ CProtoBufPoolObj & operator = ( CProtoBufPoolObj const &x );
+
+public:
+ CProtoBufPoolObj() { m_pMsg = CProtoBufMsg<TMsg>::AllocProto(); }
+ ~CProtoBufPoolObj() { CProtoBufMsg<TMsg>::FreeProto( m_pMsg ); }
+
+ operator TMsg & () { return *m_pMsg; }
+};
+
+
+} // namespace GCSDK
+
+// memdbgon is only supposed to be on in cpp files, turn it off in case the next thing includes has a conflict with it
+#include "tier0/memdbgoff.h"
+
+#endif // MSGPROTOBUF_H