summaryrefslogtreecommitdiff
path: root/public/gcsdk/msgprotobuf.h
blob: 0d5100305c03e0b9ec2335aa1df129b7017b4ac3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
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