summaryrefslogtreecommitdiff
path: root/public/gcsdk/sharedobject.h
blob: d1bfdd36443f4e2a9a61c8d4f043eb1062bd06fa (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
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for objects that are kept in synch between client and server
//
//=============================================================================

#ifndef SHAREDOBJECT_H
#define SHAREDOBJECT_H
#ifdef _WIN32
#pragma once
#endif

// ENABLE_SO_OVERWRITE_PARANOIA can be set to either 0 or 1. If enabled, it will add
// extra fields to every CSharedObject instance to try and detect overwrites at the
// cost of additional runtime memory.
#define ENABLE_SO_OVERWRITE_PARANOIA				0

// ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA can be set to either 0 or 1. If enabled, it
// will add extra fields to every CSharedObject instance to try and detect issues with
// constructions/destruction (ie., double-deletes, etc.), including reference counting.
#define ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA		(defined( STAGING_ONLY ))

#include "tier0/memdbgon.h"

namespace GCSDK
{

class CSQLAccess;
class CSharedObject;
typedef CSharedObject *(*SOCreationFunc_t)( );
class CSharedObjectCache;



//----------------------------------------------------------------------------
// Purpose: Abstract base class for objects that are shared between the GC and
//			a gameserver/client. These can also be stored in the database.
//----------------------------------------------------------------------------
class CSharedObject
{
	friend class CGCSharedObjectCache;
	friend class CSharedObjectCache;
public:

#ifdef GC
	virtual ~CSharedObject()
	{
#if ENABLE_SO_OVERWRITE_PARANOIA
		m_pThis = NULL;
#endif // ENABLE_SO_OVERWRITE_PARANOIA

#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
		AssertMsg2( m_nRefCount == 0, "Deleting shared object type %d with refcount %d", m_nSOTypeID, m_nRefCount );
		
		m_nSOTypeID = -1;
		m_nRefCount = -1;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
	}

#if ENABLE_SO_OVERWRITE_PARANOIA
	CSharedObject *m_pThis;
#endif // ENABLE_SO_OVERWRITE_PARANOIA

#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
	int m_nRefCount;
	mutable int m_nSOTypeID;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA

	void Check() const
	{
#if ENABLE_SO_OVERWRITE_PARANOIA
		Assert( m_pThis == this );
#endif // ENABLE_SO_OVERWRITE_PARANOIA

#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
		Assert( m_nRefCount >= 0 );

		if ( m_nSOTypeID == kSharedObject_UnassignedType )
		{
			m_nSOTypeID = GetTypeID();
		}

		Assert( m_nSOTypeID >= 0 );
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
	}
#else
	virtual ~CSharedObject() {}
#endif

	virtual int GetTypeID() const = 0;
	virtual bool BParseFromMessage( const CUtlBuffer & buffer ) = 0;
	virtual bool BParseFromMessage( const std::string &buffer ) = 0;
	virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) = 0;
	virtual bool BIsKeyLess( const CSharedObject & soRHS ) const = 0;
	virtual void Copy( const CSharedObject & soRHS ) = 0;
	virtual void Dump() const = 0;
	virtual bool BShouldDeleteByCache() const { return true; }
	virtual CUtlString GetDebugString() const { return PchClassName( GetTypeID() ); };

	bool BIsKeyEqual( const CSharedObject & soRHS ) const;

	static void RegisterFactory( int nTypeID, SOCreationFunc_t fnFactory, uint32 unFlags, const char *pchClassName );
	static CSharedObject *Create( int nTypeID );
	static uint32 GetTypeFlags( int nTypeID );
	static const char *PchClassName( int nTypeID );
	static const char *PchClassBuildCacheNodeName( int nTypeID );
	static const char *PchClassCreateNodeName( int nTypeID );
	static const char *PchClassUpdateNodeName( int nTypeID );

#ifdef GC
	virtual bool BIsNetworked() const { return true; }
	virtual bool BIsDatabaseBacked() const { return true; }
	virtual bool BYieldingAddToDatabase();
	virtual bool BYieldingWriteToDatabase( const CUtlVector< int > &fields );
	virtual bool BYieldingRemoveFromDatabase();

	virtual bool BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess ) { return false; }
	virtual bool BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields ) { return false; }
	virtual bool BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess ) { return false; }

	virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const = 0;
	virtual bool BAddToMessage( std::string *pBuffer ) const = 0;
	virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const = 0;
	virtual bool BAddDestroyToMessage( std::string *pBuffer ) const = 0;

	virtual bool BParseFromMemcached( CUtlBuffer & buffer ) { return false; }
	virtual bool BAddToMemcached( CUtlBuffer & bufOutput ) const { return false; }

	bool BSendCreateToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const;
	bool BSendDestroyToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const;

#ifdef DBGFLAG_VALIDATE
	virtual void Validate( CValidator &validator, const char *pchName );
	static void ValidateStatics( CValidator & validator );
#endif

protected:
/*
	// Dirty bit modification. Do not call these directly on SharedObjects. Call them
	// on the cache that owns the object so they can be added/removed from the right lists.
	virtual void DirtyField( int nField ) = 0;
	virtual void MakeDatabaseClean() = 0;
	virtual void MakeNetworkClean() = 0;
*/
#endif // GC

#ifdef GC
	CSharedObject()
	{
#if ENABLE_SO_OVERWRITE_PARANOIA
		m_pThis = this;
#endif // ENABLE_SO_OVERWRITE_PARANOIA

#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
		m_nRefCount = 0;
		m_nSOTypeID = kSharedObject_UnassignedType;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
	}
#endif	

private:
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
	enum { kSharedObject_UnassignedType = -999 };
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA

	struct SharedObjectInfo_t
	{
		SOCreationFunc_t m_pFactoryFunction;
		uint32 m_unFlags;
		const char *m_pchClassName;
		CUtlString m_sBuildCacheSubNodeName;
		CUtlString m_sUpdateNodeName;
		CUtlString m_sCreateNodeName;
	};
	static CUtlMap<int, SharedObjectInfo_t> sm_mapFactories;

public:
	static const CUtlMap<int, SharedObjectInfo_t> & GetFactories() { return sm_mapFactories; }
};

typedef CUtlVectorFixedGrowable<CSharedObject *, 1> CSharedObjectVec;

#ifdef GC

//this class manages the stats for the shared objects, such as how many exist, how many have been sent, how many have been created, how many destroyed
class CSharedObjectStats
{
public:

	CSharedObjectStats();

	//called to register a shared object class given a type ID and name. This will return false if
	//a type of this ID is already registered
	void	RegisterSharedObjectType( int nTypeID, const char* pszTypeName );

	//called to track when a shared object is created/destroyed to update the running total
	void	TrackSharedObjectLifetime( int nTypeID, int32 nCount );

	//called when a shared object is created to track its creation
	void	TrackSharedObjectSendCreate( int nTypeID, uint32 nCount );
	//called when a shared object is destroyed to track counts
	void	TrackSharedObjectSendDestroy( int nTypeID, uint32 nCount );

	//tracks a new subscription
	void	TrackSubscription( int nTypeID, uint32 nFlags, uint32 nNumSubscriptions );

	//called when a shared object is sent
	void	TrackSharedObjectSend( int nTypeID, uint32 nNumClients, uint32 nMsgSize );

	//reset the stats for all the caches
	void	ResetStats();

	//called to start/stop collecting stats
	void	StartCollectingStats();
	void	StopCollectingStats();
	bool	IsCollectingStats() const					{ return m_bCollectingStats; }

	//called to report the currently collected stats
	void	ReportCollectedStats() const;

private:

	struct SOStats_t
	{
		SOStats_t();
		void		ResetStats();
		
		//the text name of this cache
		CUtlString	m_sName;
		//the number of outstanding SO cache objects of this type. This is not cleared
		uint32		m_nNumActive;
		//the type ID of this stat
		int			m_nTypeID;

		//the total number that have been created/destroyed. Active is difference between these
		uint32		m_nNumCreated;
		uint32		m_nNumDestroyed;
		//how many bytes we have sent (raw, and multiplexed to multiple subscribers)
		uint64		m_nRawBytesSent;
		uint64		m_nMultiplexedBytesSent;
		//the number of sends since our last clear
		uint32		m_nNumSends;
		//how many subscriptions we have gotten by various subscription types
		uint32		m_nNumSubOwner;
		uint32		m_nNumSubOtherUsers;
		uint32		m_nNumSubGameServer;
	};
	
	//sort function that controls the order that the SO stats are presented in the report
	static bool SortSOStatsSent( const SOStats_t* pLhs, const SOStats_t* pRhs );
	static bool SortSOStatsSubscribe( const SOStats_t* pLhs, const SOStats_t* pRhs );

	//to compact the memory space for wide ranging type IDs, the list of stats has two components, an array that can be looked up
	//via the type ID which maps to a stat index (or invalid index if no mapping is registered)
	static const uint16			knInvalidIndex = ( uint16 )-1;
	CUtlVector< uint16 >		m_vTypeToIndex;
	CUtlVector< SOStats_t >		m_Stats;
	
	//are we currently collecting stats or not?
	bool			m_bCollectingStats;
	//the time that we've been collecting
	CJobTime		m_CollectTime;
	uint64			m_nMicroSElapsed;
};

//global stat tracker
extern CSharedObjectStats g_SharedObjectStats;

#endif


//----------------------------------------------------------------------------
// Purpose: Templatized function to use as a factory method for 
//			CSharedObject subclasses
//----------------------------------------------------------------------------
template<typename SharedObjectSubclass_t>
CSharedObject *CreateSharedObjectSubclass()
{
	return new SharedObjectSubclass_t();
}

// Version that always asserts and returns NULL, for SOs that should not be auto-created this way
template<int nSharedObjectType>
CSharedObject *CreateSharedObjectSubclassProhibited()
{
	AssertMsg( false, "Attempting to auto-create object of type %d which does not allow SO-based creation", nSharedObjectType );
	return NULL;
}

#ifdef GC
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass, flags ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, (flags), #derivedClass )
// GC only -- sharedobjects that cannot be auto-created by the SO code, and might not have a specific derived class that
// publicly implements the interface
#define REG_SHARED_OBJECT_SUBCLASS_BY_ID_NOCREATE( strName, nTypeID, flags ) \
	GCSDK::CSharedObject::RegisterFactory( nTypeID, CreateSharedObjectSubclassProhibited<nTypeID>, (flags), strName )
#else
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, 0, #derivedClass )
#endif

} // namespace GCSDK


#include "tier0/memdbgoff.h"

#endif //SHAREDOBJECT_H